Clicking a project's name opens an in-app detail view (back button returns to the grid) with: - header: name, path, branch, dirty - Branch / Worktrees: each `git worktree list` entry (branch or detached@head, main/current/locked/prunable tags, path) — a single-worktree repo shows its branch; a multi-worktree repo shows them all - Active sessions (N): detailed rows (status, devices watching, started-ago) with Open + Kill - the Claude/Codex/VS Code launcher row Backend: src/http/worktrees.ts (parseWorktrees pure + listWorktrees via execFile, no shell, best-effort); buildProjectDetail(cfg, path, liveSessions) in projects.ts (validates absolute existing dir, reuses branch/dirty/session helpers, uncached — detail is on-demand and wants fresh state); read-only GET /projects/detail?path= (no Origin guard, like /projects; 400 missing / 404 invalid). Types: WorktreeInfo, ProjectDetail. Frontend: detail view + navigation in projects.ts (grid<->detail), project name becomes a link, Kill via DELETE /live-sessions/:id. Tests +21 (452 total): worktree parser, buildProjectDetail (validation, non-git, session merge, real git-init worktree), renderProjectDetail. worktrees.ts 100% / projects.ts 93.8% cov. Verified in-browser: opened a Claude session, then the detail showed its branch+worktree and the live session with Open/Kill.
49 KiB
Web Terminal 实施进度日志 (Progress Log)
用途: 记录 PLAN.md 各任务的完成情况,作为跨会话记忆文件。 新会话开工前必读本文件。只记已发生的事实(含失败、跳过),不预填未来,不写愿望。 谁来写(G1): 本文件仅由 orchestrator(主会话)写入 —— 它不在任何任务的
Owns:里, 防止并行 subagent 并发写冲突。被派的 subagent 不写本文件,而是把日志条目作为返回结果交回, 由 orchestrator 在每批 agent 返回后立即追加(不要攒到最后)。 完整维护规则见 CLAUDE.md → Development Workflow。
状态图例
| 标记 | 含义 |
|---|---|
[ ] |
TODO — 未开始 |
[~] |
IN PROGRESS — 进行中 |
[x] |
DONE — 完成且已验证 |
[!] |
BLOCKED — 受阻(须在详细日志写明阻塞原因) |
当前焦点 (Current Focus)
新会话读到的第一块。保持准确,只描述"此刻"。
✅ v0.6 Project Manager 项目工作台(完成 — 2026-06-30,分支 v0.6-projects)
-
全部 6 任务完成并验证(P1–P6,多 agent 编排)。398 测试全绿(自 341 +57)· 双 typecheck 干净 ·
build:webOK · 覆盖率 90.98/81.95/89.28/93.16(阈值 80×4 通过)· 真浏览器 smoke 通过。 -
编排方式: ultracode
Workflow动态编排——Build(P2 后端 ∥ P5 前端,module-builder)→ Wire(P4 端点 ∥ P6 接线)→ Review(module-reviewer验收 +typescript-reviewer+security-reviewer,report-only 并行)→ 对每个 HIGH/CRITICAL 对抗式 Verify(module-reviewer)→ Fix(module-builder仅改已确认项)。P6 首跑因 transientserver_error挂掉(未落任何改动)→ stop +resumeFromRunId恢复:P2/P4/P5 命中缓存秒回,P6 重跑成功。 -
交付:
- P2
src/http/projects.ts(15 测试,93.37% 覆盖):parseGitHead(纯) +buildProjects(cfg, liveSessions);BFS 扫 .git(深度上限、跳过 node_modules/dotdir/symlink、命中即停下钻)→ 读.git/HEAD分支 +git status --porcelaindirty(execFile无 shell、2s 超时、并发 8)→ 合并~/.claude/projectscwd(复用history.listSessions)→ 按 cwd 前缀归并 live sessions(每次 fresh)→ TTL 缓存(仅缓存磁盘发现)+ in-flight 去重。 - P5
public/projects.ts(纯 helper 30+ 测试) + style 追加:mountProjects(镜像mountLauncher);1:N 卡片(分支 chip / dirty 点 / session 行 /+ start claude here)、搜索过滤、收藏置顶(localStorage);.home-seg段控样式也在此定义。 - P4
src/server.ts:GET /projects(只读、无 Origin 守卫、try/catch 兜底[])+ 5 集成测试(真起 server)。 - P6
public/tabs.ts:openProject(同名#n后缀、addEntry(null,label,cwd,'claude\r'))+countOpenWithTitlePrefix+ 首页 Sessions↔Projects 段控(updateHomeView);7 新测试。
- P2
-
Review 12 findings → 4 HIGH 全确认并修复: ① A3 关键:
openProject/newTabForResume缺回车\r(claude 不自动执行)→ 补\r;② 段控被定位面板盖住 → 面板inset:44px 0 0 0;③mergeHistory串行 I/O →mapWithConcurrency;④ discover 并发缓存击穿 → in-flight promise 去重。 -
Orchestrator 追加加固(MEDIUM/LOW,本人直接改 + 补测): 前端
fetchProjects加normalizeProject(API 响应边界校验,缺sessions不再 TypeError)+getFavs过滤非字符串(localStorage 防污);config.parseProjectRoots相对路径 fail-fast(+ config 补 v0.6 解析测试,config.ts 95.87%→98%)。style.css1143 行 > 800 上限属既有债(v0.6 前已超),esbuild 仍打单文件,留作后续拆分。 -
真浏览器 smoke(
PROJECT_ROOTS=~/Documents/git,83 真 repo): 首页段控 Sessions/Projects 切换 ✓;Projects 面板 83 卡片(web-terminal显示branch:v0.6-projects+ dirty 正确,含feat/...斜杠分支)+ 搜索框 ✓;点web-terminal卡 → 开 tab 标签web-terminal、首页隐藏、终端出现 claude 启动界面(A3 端到端)✓;console 仅既有/favicon.ico404 + apple-mobile 警告,无 v0.6 报错。 -
待办: 真机/手机验收(同历史 F4/F7 遗留);可选 server.ts 抽 routes;
style.css拆分。 -
当前阶段: v0.3 全部完成(H1–H4 + M3/M6/M7 已并入 main;O2 历史浏览 在分支
o2-history,待合并)。- ✅ Step1 前端快赢 · ✅ H2/H4 状态感知 · ✅ H3 远程批准 · ✅ H1 tmux 保活 · ✅ M3/M7/M6 · ✅ O2 历史会话浏览/resume · ✅ tech-debt 清理(@ts-ignore 去掉)· ✅ UI:终端不再打印 Connecting/Connected(靠标签点)。
- ✅ 现代化 UI 主题(design-token 调色板/靛蓝强调色/圆角芯片标签+键栏/毛玻璃弹层)· ✅ 键栏功能字幕(每键下方标作用)+ 新增 ^R/^L/^D(共 17 键)· ✅ ⌨ 快捷键速查弹层(
public/shortcuts.ts)。 - ✅ v0.4 多设备共享会话:放宽不变式 #5 —— 一个 session 可挂多个 WS 客户端(镜像;输出/退出/状态广播,谁都能打字)。
GET /live-sessions+ 打开应用自动把主机活动会话显示为 tab;🔗分享二维码 /?join=<id>加入指定会话。 - ✅ 镜像尺寸 = 最近活跃端优先(latest-writer-wins):共享 PTY 只能一个尺寸,之前取最小值导致宽屏桌面被 iPad 夹小(iPad 全屏、桌面留黑边)。改为"最近 fit/聚焦的设备决定尺寸",你正在用的设备永远全屏;attach/detach/
blur都不改尺寸,前端在 pane 显示 + 窗口聚焦时重发 dims(切设备即夺回全屏)。实测两端:200×50 → iPad 100×40 → 桌面 refit 200×50 → iPad blur 不变。后端纯客户端实测镜像输出 + 共享输入通过。 - ✅ 🗂 会话管理页(
/manage.html独立页,非弹窗):实时预览缩略图网格 —— 每张卡片用只读 xterm 渲染该 session 当前屏幕(像截图,缩放),一眼看出每个 session 在干嘛;RingBuffer.tail()+GET /live-sessions/:id/preview(不 attach、不影响观看数/回收),每 4s 刷新;可点缩略图/Open 进入、Kill / 批量 Kill。实测 3 个 session 缩略图(top / git log / ls)彩色可辨,Kill 3→2→0。 - ✅ v0.5 首页 = 会话选择器(launcher):打开应用不再自动建/恢复 tab,而是落到选择器 —— 主机所有运行中 session 的实时缩略图网格,用户自己点开(回放完整 scrollback)或
+ New session;关掉最后一个 tab 回到选择器。public/launcher.ts,复用.mg-*卡片样式。实测:首次加载 0 tab + 缩略图;Open→建 tab、选择器隐藏;关最后一个→选择器回来。 - ⬜ 仅剩:O1 token 认证(可选,默认关)+ F4/F7 真机验收(局域网/手机)。
- 225 测试全绿;工具栏 🔍搜索 ⚙设置 ▦仪表盘 🕘历史 ⌨快捷键 🔗分享 🗂管理 📱QR。
- 下一步: O1(可选)或真机验收。
- v0.2/v0.1 历史见下方条目。
-
Wave: W0–W5 基本完成。v0.1 功能完整、测试与真机浏览器验证通过。仅剩 2 项需用户真设备。
-
进度: 20/21 任务(T19 待真手机)· 187 自动化测试全绿 · 真 PTY 端到端验证 · 真浏览器 smoke 通过。
-
验收 F1–F9: ✅ F1/F2(浏览器 smoke:渲染+输入输出+全彩)· ✅ F3(resize 无错)· ⬜ F4 需局域网真机 · ✅ F5/F6(集成 ⑤⑥ 回放 CJK/ANSI)· ⬜ F7 需真手机(软键盘栏)· ✅ F8(浏览器 exit→重连提示)· ✅ F9(集成+curl 401)。
-
下一步(交用户): 真手机/另一台电脑开
http://<你IP>:3000实测 F4(局域网可用)与 F7(Esc/Shift+Tab/方向键等触摸键生效)。 -
阻塞: 无(F4/F7 非阻塞,属需物理设备的人工验收)。
-
次要 tech-debt: main.ts 1 处
@ts-ignore(CSS import);未来加*.cssd.ts。 -
最后更新: 2026-06-20(并入 review 全面修复,见下方条目)
维护性修复条目 (Review Fixes)
2026-06-20 · 按 REVIEW_REPORT 逐项修复(安全/架构/质量/测试)
- 状态:
[x]DONE(typecheck 干净 · 341 测试全绿(16 文件,自 228 起 +113)·build:web通过 · 覆盖率 90.3/80.7/87.6/92.4 ≥80 阈值已在vitest.config.ts强制)。 - 来源: 4 个 agent 并行 review 产出
docs/REVIEW_REPORT.md;用户决定修全部四档,/sessions保持现状仅加说明,clientDims/blur死状态彻底删除。计划见~/.claude/plans/piped-marinating-parrot.md。 - 🔴 关键:
- 多设备审批竞态(
server.tsws.close):仅当最后一个 client 离开时才resolvePending—— 关一个镜像不再取消另一台正在看的待批准。 - 会话无上限 DoS:新增
Config.maxSessions(默认 50,envMAX_SESSIONS)+manager.assertUnderSessionCap(),超限抛错复用 M4 的 exit(-1) 路径。 - 信号处理器泄漏:
onSignal/onUncaught提具名引用,close()里process.off全移除(去掉测试setMaxListeners(50)遮掩)。 - 前端
initialInput700ms 定时器:跟踪initialInputTimer、dispose()清理、disposed守卫、INITIAL_INPUT_DELAY_MS常量。 tabs.ts addEntry的null as unknown as TerminalSession类型洞:先建 session 再建 entry。
- 多设备审批竞态(
- 🟠 应修: 安全头中间件(无 helmet)+
DELETE /live-sessions[/:id]Origin/CSRF 守卫(403);history.ts改fs/promises(listSessions异步,/sessionsawait);彻底删除clientDims+blur(types/session/protocol/server/manager 注释/terminal-session.hide + 相关测试);WS 连接级限频(maxMsgsPerSec默认 2000,超限丢帧不断连);/sessions保持行为,加注释 +TECH_DOC §7记录已接受风险。 - 🟡 测试: 新增
test/{tmux,preview-grid,terminal-session,tabs}.test.ts(后两者 jsdom + mock WebSocket);扩history/config/manager/integration覆盖 killById/handleHookEvent/maxSessions/resolveUseTmux/live-sessions/preview-404/approve-reject/origin-guard/限频/信号泄漏回归。 - ⚪ 卫生:
parsePositiveInt→parseNonNegativeInt;ALLOWED_ORIGINS scheme 校验;server.ts日志注入 sanitize +isLoopback健壮化(127.0.0.0/8 + IPv4-mapped);运维常量(PERM_TIMEOUT_MS/REAP_INTERVAL_MS/PREVIEW_BYTES)入 Config;抽public/preview-grid.ts消除 launcher/manage 重复(类型复用LiveSessionInfo);ARCHITECTURE §8 记录运行时句柄可变例外(H6)+ #5/#7/#8 更新;CLAUDE.md「auto-show as tabs」→ v0.5 launcher;「min across clients」陈旧注释清扫。 - 验证:
npm run typecheckPASS ·npx vitest run341/341 ·npm run build:webOK ·npx vitest run --coverage退出 0(阈值 80×4)。 - 决策 / 偏离: 覆盖率
include收窄到src/**+ 四个有逻辑的前端模块,排除纯 DOM 接线/入口胶水(E2E 范畴),未下调任何阈值。未把审批状态机搬进 manager(报告 #10)—— close-handler 修法已正确最小,server.ts抽 routes 留作可选后续。 - 遗留 / 待办: 真机验收 F4/F7(同 v0.1 遗留);可选 server.ts 抽
http/routes.ts;胶水文件若要纳入覆盖率需补 E2E。 - commit: (本次提交)
任务总览 (Task Overview)
任务定义见 PLAN.md;按 Wave 分组。
Steps完成数 = PLAN 中该任务的勾选项。 多 agent 并行:同一 Wave 内任务互不依赖,可并行;跨 Wave 按依赖推进。
| 任务 | Wave | 模块 | 状态 | Steps (完成/总) | 完成日期 |
|---|---|---|---|---|---|
| T1 | W0 | 脚手架 + 装齐依赖 | [x] |
5/5 | 2026-06-16 |
| T2 | W0 | src/types.ts 共享契约(冻结源) |
[x] |
8/8 | 2026-06-16 |
| T3 | W0 | mock IPty 测试替身 | [x] |
2/2 | 2026-06-16 |
| T4 | W1 | config.ts |
[x] |
5/5 | 2026-06-16 |
| T5 | W1 | protocol.ts |
[x] |
4/4 | 2026-06-16 |
| T6 | W1 | ring-buffer.ts |
[x] |
4/4 | 2026-06-16 |
| T7 | W1 | http/origin.ts |
[x] |
3/3 | 2026-06-16 |
| T8 | W1 | public/index.html |
[x] |
1/1 | 2026-06-16 |
| T9 | W1 | public/style.css |
[x] |
1/1 | 2026-06-16 |
| T10 | W1 | public/keybar.ts |
[x] |
4/4 | 2026-06-16 |
| T11 | W1 | public/main.ts |
[x] |
6/6 | 2026-06-16 |
| T12 | W2 | session/session.ts |
[x] |
7/7 | 2026-06-16 |
| T13 | W3 | session/manager.ts |
[x] |
6/6 | 2026-06-16 |
| T14 | W4 | server.ts 接线 |
[x] |
6/6 | 2026-06-17 |
| T15 | W4 | 集成 / E2E 测试 | [x] |
2/2 | 2026-06-17 |
| T16 | W5 | 验收 F1–F3(终端/TUI/resize) | [x] |
3/3 | 2026-06-17 |
| T17 | W5 | 验收 F4+F9(LAN/Origin) | [~] |
1/2 | 2026-06-17 |
| T18 | W5 | 验收 F5+F6(保活/回放) | [x] |
2/2 | 2026-06-17 |
| T19 | W5 | 验收 F7(移动键栏) | [ ] |
0/1 | 需真机 |
| T20 | W5 | 验收 F8(退出处理) | [x] |
2/2 | 2026-06-17 |
| T21 | W5 | 安全核对 + README + LOG 收尾 | [x] |
3/3 | 2026-06-17 |
验收清单映射 (Acceptance — TECH_DOC §8)
功能验收独立于阶段勾选;F* 全绿才算 v0.1 完成。
| 编号 | 功能 | 状态 | 验证方式 / 备注 |
|---|---|---|---|
| F1 | 交互式终端(ls/补全/Ctrl+C/历史) | [x] |
浏览器 smoke:输入→输出往返 |
| F2 | 全彩 + 全屏程序(vim/top/claude) | [x] |
浏览器 smoke:彩色提示渲染(深 TUI 同理) |
| F3 | 终端自适应(resize 不错位) | [x] |
viewport 变更存活、无 error |
| F4 | 局域网访问(手机/他机) | [ ] |
需真机(同 WiFi 开 http://:3000);Origin 白名单已含网卡 IP |
| F5 | 会话保活(断开重连仍在跑) | [x] |
集成 ⑤;tmux 重启保活(H1) |
| F6 | 重连回放(环形缓冲) | [x] |
集成 ⑥:ANSI/中文不乱码(M2) |
| F7 | 移动快捷键栏 | [ ] |
需真手机(触摸键生效、不弹软键盘) |
| F8 | 进程退出处理 | [x] |
浏览器 smoke:exit→重连提示;detach 后退出 L1 |
| F9 | Origin 校验(401) | [x] |
集成 ①②;curl 坏 Origin→401;/term 限制(L3) |
详细日志 (Detailed Log)
倒序,最新在最上。每个子任务一条。复制下方模板填写。
2026-06-30 · v0.6 项目详情页 — branch/worktree + 详细 active session
- 状态:
[x]DONE。452 测试全绿(+21)· 双 typecheck 干净 ·build:webOK · 覆盖率 90.16/81.32(worktrees.ts100% /projects.ts93.8%)· 真端到端(curl + 真浏览器)验证通过。 - 需求(用户): 每个 project 的详情页——详细显示具体 active 的 session,以及具体的 branch 或 worktree。
- 设计: 点项目卡的名字进入应用内详情视图(
renderProjectDetail,在 Projects 面板内切换,带"← All projects"返回)。分区:① 头部(名/路径/branch chip/dirty)② Branch / Worktrees(git worktree list每条:branch 或detached @ head+ main/current/locked/prunable 标签 + 路径;单 worktree 即显示分支)③ Active sessions(N)(每条:状态点 + 标题 +状态·👁设备数·started 多久前+ Open + Kill ✕)④ Start 启动器行(复用 Claude/Codex/Code)。再进 Projects 总是回到网格(不残留详情)。 - 后端: 新增
src/http/worktrees.ts(parseWorktrees(纯)+listWorktrees,git worktree list --porcelain,execFile 无 shell,2s 超时,best-effort→[]);projects.ts加buildProjectDetail(cfg, path, liveSessions)(校验绝对存在目录→否则 null;复用 readBranch/readDirty/hasGitEntry/matchSessions;不缓存,详情按需要新鲜)。src/server.tsGET /projects/detail?path=(只读、无 Origin 守卫,同 /projects;path 缺→400、非法/不存在→404)。types 加WorktreeInfo/ProjectDetail。 - 前端(
public/projects.ts+ style):fetchProjectDetail/killSession;makeProjectCard加可选onOpenDetail(名字变可点链接.proj-name-link);mountProjects加 grid↔detail 视图切换 + 自动刷新路由(详情每 5s 刷新);Kill 走DELETE /live-sessions/:id(同源带 Origin)。详情视图样式.proj-detail*/.proj-wt-*/.proj-dsession*。session meta 在 status=unknown 时省略状态点(不显示· ·)。 - 测试(+21):
test/worktrees.test.ts(parse:单/多/detached/locked/prunable/无尾空行/空);projects.test.tsbuildProjectDetail(相对/不存在/文件→null、非 git 目录、假 repo 读 branch、session 按 cwd 前缀归并、真 git init worktree(realpath 规避 /var symlink));projects-panel.test.tsrenderProjectDetail(null→not found+Back、头部、worktree 行+current tag、detached、session Open/Kill 接线+忽略 exited、空态+启动器、非 git)+ 名字链接。 - 验证:
npx tsc(双)干净;npx vitest run --coverage452/452(≥80×4);真浏览器:开 web-terminal 的 Claude session→进详情→显示 branchv0.6-projects(MAIN/CURRENT)+ Active sessions(1)(web-terminal,👁1,Open/✕)+ Start 启动器 + 返回。curl/projects/detail返回 worktree(main/current/head)+ 缺/相对 path→400/404。 - 安全:
/projects/detail只读(git branch/status/worktree-list,execFile 无 shell + 超时),path 校验绝对存在目录;与 /projects 同威胁模型(不新增可变更面)。 - commit: (本次提交)
2026-06-30 · v0.6 项目卡启动器 — Claude · Codex · VS Code logo 按钮
- 状态:
[x]DONE。覆盖率 90.6/81.48/88.71/92.71(≥80×4)· 双 typecheck 干净 ·build:webOK · 真浏览器 + 真 VS Code 端到端验证通过。 - 需求(用户): 把卡片上大的"+ start claude here"换成 Claude logo;新增 VS Code logo(点击在主机打开 VSC)与 Codex logo 按钮;logo 编排要"符合应用性"。
- 设计: 每张项目卡的动作区改为启动器行——3 个品牌 logo 图标按钮(logo 在上、小字标签在下、touch 友好、各带 tooltip),有运行中 session 时 session 行列在启动器上方。Claude=珊瑚色爆发、Codex=OpenAI blossom(近白)、Code=VS Code 蓝丝带;logo 用
fill=currentColor,颜色由 CSS 品牌色驱动。 - 行为: Claude→新终端 tab 跑
claude;Codex→新终端 tab 跑codex(复用openProject(path,name,cmd),onOpenProject加cmd?参数);VS Code→POST /open-in-editor让主机跑code <path>(文件在主机,故在主机开;不分查看设备)。主机已确认装有 claude/codex/code。 - 活跃高亮(用户追加): 项目若有活跃 Claude session(运行中且 hook 状态 ≠
unknown——纯 shell/codex 保持 unknown 不算),Claude logo 按钮珊瑚色高亮(coral ring + 淡底 + 浅色标签);>1 个时角标显示数量。activeClaudeCount(project)判定。 - 改动:
- 新增
public/icons.ts(simple-icons 的 Claude/OpenAI/VSCode 单路径 SVG,脚本生成避免手抄出错)。 public/projects.ts:makeLauncherRow/makeLauncher+openProjectInEditor(fetch/open-in-editor);ProjectsHooks.onOpenProject加cmd?;导出makeProjectCard便于测试;删旧.proj-new用法。public/tabs.ts透传 cmd。public/style.css新增.proj-launchers/.proj-launch*(品牌色),删旧.proj-new规则。- 后端:
src/http/editor.tsopenInEditor(cfg,path)(校验绝对路径+存在+目录→execFile(无 shell)启动,best-effort,error 事件兜底+unref);src/server.tsPOST /open-in-editor(Origin 守卫,与 DELETE 同档);Config.editorCmd(EDITOR_CMD,默认code)入 types/config。 - 测试: 新增
test/editor.test.ts(校验 400/404 +editorCmd='true'成功 204);config.test.ts加 editorCmd;projects-panel.test.ts加 makeProjectCard 启动器行(3 按钮/logo/Claude·Codex cmd)。
- 新增
- 验证:
npx tsc(双)干净;npx vitest run --coverage全绿(≥80×4);build:webOK。真端到端:curl/open-in-editor无 Origin→403、缺/相对/不存在 path→400/400/404、合法 dir→204 且 VS Code(Electron pid)实际打开;真浏览器 Projects 视图每卡 3 logo 按钮(珊瑚/白/蓝,标签 Claude/Codex/Code,tooltip 正确)。 - 安全:
/open-in-editor状态变更→Origin 守卫(CSRF);execFileargv 传参无 shell 注入面;path 校验为绝对存在目录;不超出"应用本就给主机 shell"的既有威胁模型。命令来自 config 非用户。 - commit: (本次提交)
2026-06-30 · v0.6 UI 打磨 — 首页段控重设计 + 选择器隐藏键栏
- 状态:
[x]DONE。415 测试全绿 · 双 typecheck 干净 ·build:webOK · 真浏览器验证通过。 - 反馈(用户): ① Sessions/Projects 段控在 UI 上"有点别扭"(原为两个独立描边方块浮在顶部);② 首页(Sessions/Projects 视图)底部那排终端快捷键栏没必要显示。
- 修复(纯前端,
public/style.css+public/tabs.ts): ①.home-seg重设计为 iOS 风分段控件——一个圆角"轨道"(surface-2底 + 圆角 999px + 内边距)内含两个 pill 按钮,激活项填 Amber;width:fit-content + margin:auto居中;去掉 first/last/only-child 圆角拼接;面板inset44→68px 适配新高度。②TabApp.updateHomeView在显示首页时给document.body加home-open类,CSSbody.home-open #keybar{display:none}隐藏键栏;打开 tab(终端激活)即恢复。 - 追加(用户反馈"段控独占一行、空白太多"): 桌面端(
@media min-width:769px)把段控position:absolute浮到右上角,与过滤框/启动器表头同一行;面板inset归 0(从顶部铺满),.launcher-head留 240px 右内边距避开段控;移动端保持居中堆叠(防止与全宽过滤框重叠)。另:首页隐藏键栏时body.home-open #term{bottom:0}收回键栏占位。纯 CSS(style.css直出,无需打包)。真浏览器实测:Projects 段控与过滤框同行(seg.top 56 / filter.top 62)、卡片在其下;Sessions 视图段控不与 New/Manage 重叠。 - commit: (本次提交)
2026-06-30 · v0.6 UX 修复 — 会话内无法返回 Projects 首页
- 状态:
[x]DONE。415 测试全绿(+3)· 双 typecheck 干净 ·build:webOK · 真浏览器验证通过。 - 问题(用户反馈): 打开某个项目进入终端 tab 后,首页的 Sessions/Projects 段控被隐藏,只能关掉所有 tab 才能回到选择器再开下一个项目——没有"返回 Projects"的入口。
- 修复(
public/tabs.ts+public/style.css): 标签栏新增 ⌂ Home 按钮;点击把首页选择器(Sessions/Projects 段控 + 对应面板)叠加显示在当前 tab 之上(homeForced标志:updateHomeView改为tabs.length===0 || homeForced时显示并隐藏所有终端 pane)。再点 ⌂ 或点任一 tab(activate重置homeForced)即回到终端;从叠加层开新会话/项目也会自动收起。关最后一个 tab 时复位homeForced。⌂ 叠加时高亮(.tab-home.active,Amber)。 - 测试(
test/tabs.test.ts+3): ⌂ 叠加首页再切回;叠加层切到 Projects 并开第二个项目后自动收起;0 tab 时 ⌂ 为 no-op。 - 验证:
npx tsc -p tsconfig.json/.web.json干净;npx vitest run415/415;真浏览器:开 shell tab→段控隐藏→点 ⌂→段控+面板叠加显示、⌂ 高亮→切 Projects 面板可见→再点 ⌂→回到终端。 - commit: (本次提交)
2026-06-30 · v0.6 Project Manager(P1–P6,多 agent Workflow 编排)
- 状态:
[x]DONE(分支v0.6-projects)。398 测试全绿 · 双 typecheck 干净 ·build:webOK · 覆盖率 90.98/81.95/89.28/93.16(≥80×4)· 真浏览器 smoke 通过。 - 目标: 首页新增 Projects 视图——发现主机上的 git 仓库,点卡片 → 开 tab(标签=repo 名、cwd=仓库、自动跑
claude);卡片显示分支/dirty/该项目运行中的 sessions(1:N)。设计见docs/FEATURE_PROJECT_MANAGER.md。 - 编排(ultracode
Workflow动态): Build(P2 后端 ∥ P5 前端)→ Wire(P4 端点 ∥ P6 接线)→ Review(module-reviewer验收 +typescript-reviewer+security-reviewer并行 report-only)→ HIGH/CRITICAL 对抗式 Verify → Fix。P6 首跑 transientserver_error挂掉(0 改动落地)→TaskStop+Workflow{resumeFromRunId}恢复;P2/P4/P5 缓存命中,P6 重跑成功。 地基 P1(types)/P3(config)上一会话已落,本会话先 commit 为dc5d073作干净基线。 - 改动:
src/http/projects.ts(新,P2):parseGitHead(纯)、buildProjects(cfg, liveSessions);内部scanRepos(BFS 深度上限/跳 node_modules·dotdir·symlink/命中即停)、readBranch、readDirty(execFilegit status,2s 超时,并发 8)、mergeHistory(复用history.listSessions)、缓存discoverProjects(TTL + in-flight 去重)、fresh session 按 cwd 前缀归并、_clearProjectCache(测试)。test/projects.test.ts(15)。public/projects.ts(新,P5):mountProjects+ 纯 helperfilterProjects/sortProjects/getFavs/saveFavs/toggleFav/normalizeProject。test/projects-panel.test.ts(36)。public/style.css追加 Projects 面板 +.home-seg段控样式。src/server.ts(P4):GET /projects(只读、无 Origin 守卫、try/catch→[])。test/integration/projects-endpoint.test.ts(5,真起 server)。public/tabs.ts(P6):openProject(同名#n、addEntry(...,'claude\r'))、countOpenWithTitlePrefix、updateHomeView+.home-seg段控。test/tabs.test.ts+7。
- Review→Fix: 12 findings,4 HIGH 全确认修复——① A3 关键缺回车
\r(openProject/newTabForResume)② 段控被定位面板遮挡(面板inset:44px 0 0 0)③mergeHistory串行→mapWithConcurrency④ discover 并发缓存击穿→in-flight 去重。Orchestrator 追加加固(MEDIUM/LOW + 测试):normalizeProject(API 边界校验)、getFavs滤非字符串、parseProjectRoots相对路径 fail-fast、config v0.6 解析补测(config.ts→98%)。 - 验证:
npx tsc -p tsconfig.json/.web.json --noEmit干净;npx vitest run398/398;npm run build:webOK;npx vitest run --coverage退出 0(阈值 80×4)。真浏览器(PROJECT_ROOTS=~/Documents/git,83 repo):段控切换 ✓、面板 83 卡片(分支/dirty/斜杠分支正确)+ 搜索 ✓、点卡 → tabweb-terminal+ 终端出现 claude 启动界面(A3 端到端)✓、console 无 v0.6 报错(仅既有 favicon 404)。 - 决策 / 偏离: 缓存只缓存磁盘发现、session 每次 fresh 归并(避免陈旧);git worktree 看板/文件树编辑器/ticket 拉取 = v0.6 明确不做(FEATURE §9)。
public/projects.ts/tabs.ts的 mount* DOM 接线沿用既有约定不纳入覆盖率(纯 helper 已测)。style.css1143>800 = 既有债,留作拆分。 - 遗留 / 待办: 真机 F4/F7(同历史);可选
server.ts抽http/routes.ts;style.css拆分。 - commit: (本次提交)
2026-06-16 · T1 脚手架 + 一次性装齐依赖
- 状态:
[x]DONE - 改动:
package.json(scripts + 全部依赖 +postinstall)、tsconfig.json(后端 NodeNext/strict)、tsconfig.web.json(前端 DOM/Bundler,当前 noEmit 仅类型检查)、vitest.config.ts(node 环境 +passWithNoTests)、.gitignore、package-lock.json、目录骨架src{,/http,/session} public test{,/helpers,/integration}(各带.gitkeep)。 - 验证:
npm install成功(运行时 72 包 + 开发包,0 漏洞)。解析版本(均比预期新的主版本):express 5.2、 @xterm/xterm 6.0、addon-fit 0.11、ws 8.21、node-pty 1.1;dev:typescript 6.0、vitest 4.1、tsx 4.22、@types/node 25。node -e require('node-pty')→ OK;真实 PTY spawnecho hello-from-pty→ 16 bytes、exitCode 0(沙箱外验证)。npm test→ exit 0(passWithNoTests)。
- 决策 / 偏离 PLAN:
- node-pty spawn-helper 修复:1.1.0 的 prebuild 自带
spawn-helper但权限-rw-r--r--(缺 +x), 导致posix_spawnp failed。加postinstall: chmod +x .../prebuilds/darwin-*/spawn-helper(对 Linux 无害,|| true), 使每次 install / 每个 worktree 自动修复。T12 spawn PTY 依赖此修复。 - 依赖解析到较新主版本(express 5 / xterm 6 / typescript 6 / vitest 4):T7/T11/T14/T15 注意 API 差异 (express 5 路由、xterm 6 import 形态)。
- 加了 PLAN 未列的脚本
dev/typecheck与postinstall(便利 + 必需修复),属合理补充,未改 PLAN 意图。
- node-pty spawn-helper 修复:1.1.0 的 prebuild 自带
- 遗留 / 待办: 无(原前端打包待决项已解决,见下补记)。
- commit:
b126cf0
2026-06-18 · v0.3 Step 5 — 中价值 M3/M7/M6(主题/仪表盘/同目录新标签)
- 状态:
[x]DONE。真浏览器验证通过。212 测试全绿。 - M3 主题/字体(commit
f79f14b):settings.ts(⚙ 面板:dark/light/solarized + 字号 A−/A+),localStorage 持久化;terminal-session.applyTheme;TabApp.applySettings 应用到所有+新标签。验证:light 主题渲染+持久化。 - M7 仪表盘(commit
f79f14b):dashboard.ts(▦ 浮层列出所有标签:连接点+文件夹+Claude 状态;点行切换;打开时每秒刷新);TabApp.snapshot()/focusTab()。验证:列出 2 标签、点击切换。 - M6 同目录新标签(commit
87e1173):title-util.cwdFromOsc7(解析 OSC 7 cwd)+4 单测;terminal-session 注册 OSC7 handler→cwd getter,新会话 attach 带 cwd;protocol/types attach.cwd(绝对路径);session/manager/server 透传到 spawn cwd;tabs+在当前 tab 的 cwd 开新标签(无则回 home)。验证:server attach.cwd 生效;cd /private/tmp→+→新标签 pwd=/private/tmp(此 zsh 发 OSC7)。 - 工具栏: 🔍搜索 · ⚙设置 · ▦仪表盘 · 📱QR。
- commit:
f79f14b(M3+M7)、87e1173(M6)
2026-06-17 · v0.3 Step 4 — H1 tmux 保活(服务重启不丢会话)
- 状态:
[x]DONE。真 tmux 集成测试验证通过(commit9099f73)。至此 H1–H4 全部高价值功能完成。 - 机制: useTmux 时把 shell 跑进
tmux new-session -A -s web_<id>;node-pty 进程只是 tmux 客户端。服务重启后,客户端带同 sessionId 重连 → manager 发现web_<id>tmux 会话仍在 → 用同 id 重建客户端(-A附着)→ 接回原 shell。 - 改动: 新增
src/session/tmux.ts(同步 CLI 封装 available/has/kill,best-effort);configuseTmux(envUSE_TMUX1/0/auto→检测 PATH);session createSession 加可选 id(重连用)+ tmux 分支 spawn +Session.tmuxName+ kill 走tmux kill-session;manager handleAttach 加 Case 3.5(重启后重附)+ shutdown 对 tmux 会话只杀客户端 pty 不杀 tmux(保活)。 - 测试: 共享集成 cfg 设
USE_TMUX:0(防 tmux 泄漏);单测 CFGuseTmux:false;集成H1(真 tmux,gate=tmux+PTY):设 shell 变量 → 重启 server → 同 id 重连 → 变量仍在(MARK-tmuxlives)。208 测试全绿,tsc 通过。 - 注: 当前后台 dev server(:3000)是 H3 之前启动的、不含 H1;要试 tmux 保活需
USE_TMUX=1 npm start。 - commit:
9099f73
2026-06-17 · v0.3 Step 3 — H3 远程批准/拒绝(不打字)
- 状态:
[x]DONE。端到端真浏览器验证通过(commit9a6150c)。 - 机制:
PermissionRequesthook 用 curl POST 到/hook/permission并阻塞等响应;服务端长挂该 HTTP 响应,推status{waiting,pending:true}给前端 → 前端显示 [✓Approve][✗Reject] 横幅 → 点按发 wsapprove/reject→ 服务端用{hookSpecificOutput.decision.behavior:allow|deny}回应被挂的请求 → curl stdout 把决定交给 Claude。全程不打字。 - 兜底: 5 分钟超时 / 断开即释放 → 回空
{},Claude 回退到自己的交互提示;web-terminal 外 curl 无 URL → no-op。 - 改动: types(ClientMessage approve/reject、status.pending)、protocol(白名单+解析)、manager(pending 透传)、server(
/hook/permission长挂 + approve/reject 路由 + resolvePending)、setup-hooks(PermissionRequest 用 held 命令)、FE(terminal-session approve()/reject()+pending、tabs 批准横幅)。 - 验证: 207 测试全绿(协议 approve/reject 单测 + 集成 ⑧ held→approve→allow);浏览器实测横幅"Claude wants to use Bash"→Approve→held 请求得到 allow→横幅消失(截图 /tmp/v03-approve.png)。
- commit:
9a6150c
2026-06-17 · v0.3 Step 2 — H2/H4 Claude 状态感知(headline)
- 状态:
[x]DONE(分支v0.3-cockpit)。端到端真浏览器验证通过。 - 机制: Claude Code hooks → 服务端
/hook侧信道 → WSstatus帧 → 标签角标实时更新。架构演进:服务端获得 hook 侧信道(终端流仍是 byte-shuttle)。 - H2 服务端(commit
a411c89):- types:
ClaudeStatus+ ServerMessagestatus;Session.claudeStatus;manager.handleHookEvent。 - session:spawn env 注入
WEBTERM_SESSION(哪个标签)+WEBTERM_HOOK_URL(本机 /hook)。 src/http/hook.ts纯映射(PreToolUse→working、PermissionRequest/permission_prompt→waiting、Stop/idle_prompt→idle);6 单测。- server:
POST /hook(仅 loopback,express.json 64kb)→ 推 status。顺带修真 bug:closeIdleConnections()让 hook keep-alive 空闲连接不再卡住 close()/SIGTERM。 - 集成 ⑦(真 PTY):attach → POST /hook → ws 收到 status(注意测试要在 fetch 前挂监听,推送是同步的)。
- types:
- H4 前端(commit
c81cebd):- terminal-session 处理 status → claudeStatus + onClaudeStatus。
- tabs:每标签 Claude 角标(⚙工作/⏳等批准/✓空闲);非活动 + waiting 标签加琥珀高亮 + 浏览器通知(首次切换请求权限)。
scripts/setup-hooks.mjs+npm run setup-hooks:幂等装/卸 ~/.claude/settings.json 的 command hook(内联 curl,web-terminal 外是 no-op),自动备份。
- 验证: 205 测试全绿;浏览器实测 PermissionRequest→⏳、PreToolUse→⚙、Stop→✓;非活动 waiting 标签琥珀+⏳(截图 /tmp/v03-status.png)。0 console error。
- commit:
a411c89(H2 服务端)、c81cebd(H4 前端+脚本)
2026-06-17 · v0.3 Step 1 — 前端快赢(键栏/搜索/链接/QR/PWA)
- 状态:
[x]DONE(分支v0.3-cockpit;纯前端,无服务端改动) - 计划:
~/.claude/plans/shimmering-wondering-island.md(v0.3 cockpit,已批准)。 - H4-keybar: 键栏加 Claude 快捷键 Esc·Esc/^O/^T/^B + 每键 tooltip(KEY_MAP 14 键;commit
eaeacf3)。 - M1 搜索:
@xterm/addon-search每终端 + 🔍 工具栏按钮 + 搜索框(Enter 下一个/Shift+Enter 上一个/Esc 关)。 - M2 可点击链接:
@xterm/addon-web-links。 - 结构: 标签栏拆成
#tabs(可滚)+#toolbar(工具按钮区),为后续 QR/设置/仪表盘复用。 - M5 QR: 📱 按钮 → 客户端渲染 location.origin 的二维码(经 LAN IP 打开即可分享)+ localhost 提示(commit
fbc218c)。 - M4 PWA: manifest + icon.svg + sw.js(network-first,不拦 /term//hook)+ 注册;可安装。
- 验证(真浏览器): 14 键栏按钮、搜索高亮命中、🔍📱 工具按钮、SW controlled、QR canvas 渲染、0 console error。199 测试全绿、双 tsc、build 通过。截图 /tmp/v03-quickwins.png、/tmp/v03-qr.png。
- commit:
eaeacf3、fbc218c
2026-06-17 · v0.2 修复 — 切换标签要点多次
- 状态:
[x]FIXED - 现象: 点标签切换常常点一两下不生效,要多点几次。
- 根因: 整个标签
draggable=true,点击时鼠标微动一点点就被当成拖拽(而非点击)→ 切换失效;且每次 activity/title/status 事件都replaceChildren()全量重建标签栏,点击瞬间元素可能被销毁。 - 修复(
tabs.ts重构渲染):① 切换改pointerdown(按下即切,先于拖拽判定,触摸也生效);② 标签 DOM 建一次,状态点/未读/标题就地更新(refreshTab),仅增删/排序/改名才全量重建;③ 关闭按钮/改名框 pointerdown stopPropagation 避免误激活。 - 验证(真浏览器): 连续单击 tab0→2→1→0 active 每次都跟随;CDP 真实点击也切换;改名/拖拽不受影响。198 测试全绿。
- commit:
e058751
2026-06-17 · v0.2 — 标签按当前文件夹自动命名
- 状态:
[x]DONE(纯前端) - 需求: 标签名用当前文件夹,易区分。
- 改动: 新增
public/title-util.ts的folderFromTitle()——解析 shell OSC 标题user@host:~/path取最后一段目录名(home 保留~);纯函数、无依赖、7 单测。terminal-session.ts把onTitleChange经此函数转为文件夹名。标题优先级:手动改名 > 文件夹名 > Term N。 - 验证(真浏览器): 198 测试全绿、tsc+build ✓。实测
cd实时更新:~→ 项目目录web-terminal→docs→~。 - commit:
4091283
2026-06-17 · v0.2 — 标签状态点 + 活动指示 + 拖拽排序
- 状态:
[x]DONE(纯前端) - 改动:
terminal-session.ts:跟踪SessionStatus(connecting/connected/reconnecting/exited)+onStatus回调。Bug 修复:onTitle之前只在 opts 里、构造函数从没绑定onTitleChange—— 自动标题原本是死代码,现已接上(实测两标签显示host:~自动标题)。tabs.ts:每标签状态点(颜色=连接态:绿/琥珀/红);未读活动(后台标签来新输出→hasActivity→蓝环,切到该标签即清);HTML5 拖拽排序(保留活动标签、持久化)。style.css:.tab-dot配色、.unread蓝环、.dragging/.drag-over反馈。
- 验证(orchestrator,真浏览器): 191 测试全绿、双 typecheck、build:web ✓。browse 实测:连接后绿点;Term1 后台收到延迟输出→未读蓝环(
unread:true);拖拽AAA|BBB→BBB|AAA且reload 后顺序+自定义名都保留。截图 /tmp/webterm-dots.png。 - commit:
b5f21e5
2026-06-17 · v0.2 — 多标签 + 标题 + Claude Code 快捷键栏
- 状态:
[x]DONE(纯前端;后端 manager 早已多会话,无需改后端/协议) - 需求: 用户要 ①多标签 ②标签加标题方便区分 ③适配平板/手机 ④移动端易触发 Claude Code 快捷键(按钮触发快捷键)。
- UX 调研(general-purpose 联网,参考 iTerm2/Warp/VS Code/Windows Terminal/Ghostty/Chrome):双击就地改名是现代主流(Warp/VS Code/Arc);自动标题用 OSC 0/2;中键关闭、× 悬停显示、省略号+tooltip、Cmd/Ctrl+数字切换是 Tier-1。验证了"OSC 自动标题 + 双击改名"方案正确。
- 改动(
public/):- 新增
terminal-session.ts:从旧 main.ts 抽出"单会话 = 1 Terminal + 1 WS + 重连",加 show/hide/fit/focus/dispose + onTitle/onActivity 回调。 - 新增
tabs.ts:TabApp 多标签管理 + 标签栏 UI。每标签独立 WS/会话(沿用 1WS↔1会话协议);自动标题(onTitleChange)+ 双击就地改名(手动>自动>Term N);中键关闭、× 按钮、省略号+tooltip;localStorage 持久化{id,title}[](兼容 v0.1 单会话 key 迁移)。关闭标签=detach(PTY 不死)。 keybar.ts:加slash键;导出KEYBAR_BUTTONS按 Claude 频率排序(Esc 标 primary);+4 单测。main.ts瘦身为引导;index.html加#tabbar;style.css重写:键栏全设备常显(桌面也可点按钮触发快捷键)、@media(pointer:coarse)触摸加大、Esc 琥珀色高亮、标签栏/改名框/活动下划线。
- 新增
- 决策:键盘快捷键(Cmd+T 等)不做——浏览器宿主会抢,改走点击/触摸 UI(对 web 终端正确取舍)。
- 验证(orchestrator,关沙箱真浏览器): 两 typecheck ✓;
npm test191 全绿(+4 keybar);build:web✓。browse 实测:双击改名 Term1→"claude"、两标签会话独立(claude | Term 2)、键栏桌面可见 10 键 Esc primary、窄屏可滚动可用。截图 /tmp/webterm-ux-desktop.png、-phone.png。 - commit:
4f66016
2026-06-17 · W5 — 验收 + 收尾(T16–T21)
- 状态: T16 ✅ · T17 部分(F9✅/F4 待真机)· T18 ✅ · T19 待真机 · T20 ✅ · T21 ✅
- T21(orchestrator):写
README.md(install/run/config/security);安全自查——无硬编码 secret(grep 空)、Origin+路径+maxPayload 校验已接线(server.ts 确认)、无 eval/exec、input 原样透传给 PTY(byte-shuttle 设计意图);npm run build→dist/server.js可产出。 - F1/F2/F3/F8 浏览器 smoke(关沙箱起 server + gstack browse 无头 Chromium):
· 加载 http://127.0.0.1:38080 → .xterm 挂载、0 console error、WS 连上、彩色状态行 + 真 zsh 彩色提示(F1/F2)。
· 聚焦
.xterm-helper-textarea输入echo hello-webterm→ 回显 + 输出hello-webterm(输入→WS→PTY→shell→输出→xterm 全链路)。截图 /tmp/webterm-smoke2.png。 · 输入exit→ 终端显示 "Process exited code=0 Press Enter to reconnect…"(F8)。 · 改 viewport(F3)→ 终端存活、无 console error。 · curl 复核:GET / 200、/build/main.js 200、坏 Origin upgrade → 401。 - 决策: F4(局域网多设备)、F7(手机软键盘栏)需物理设备,无法在本机无头验证 → 交用户真机。其余 7/9 F 项已自动化验证。
- 遗留: T19(F7)待真手机;F4 待局域网真机;main.ts 的 css
@ts-ignore小债。 - commit:
81585ad(README/安全)+ 本条 LOG
2026-06-17 · W2/W3/W4 — 会话核心 + 集成(T12/T13/T14/T15)
- 状态:
[x]ALL DONE · 串行(各有依赖),单 builder/任务 - T12
session/session.ts(opus,19 测试):createSession/attachWs/detachWs/writeInput/resize/kill;onData→buffer+lastOutputAt+转发;onExit→exitedAt/exitCode+发exit+注入onExit;spawn 失败抛(M4);sendIfOpen 守卫 readyState(M5);detach 不杀 PTY;exit 后 write/resize 忽略(L4);detach 后退出保留会话(L1)。测试vi.mock('node-pty')→mock IPty,沙箱内可跑。 - T13
session/manager.ts(sonnet,21 测试):handleAttach 四路径(新建/存活/已退出 L1/查不到);reapIdlenow-max(detachedAt,lastOutputAt)>idleTtl(M3);shutdown 全 kill;注入 onExit 在线退出删表、detach 后退出保留待 L1(L2)。 - T14
server.ts(sonnet,~200 行):Express 静态 public/+build/;WebSocketServer({noServer,maxPayload})(L5);upgrade 门(非 wsPath→destroy、坏 Origin→401、过→handleUpgrade,L3);connection 等 attach,try/catch→spawn 失败 exit(-1)+关连接(M4);message 路由;close→detach(不杀);SIGINT/TERM→shutdown;reapIdle 定时器;safeSend 守卫(M5)。导出 startServer 供 T15。 - T15 集成(sonnet,6 用例):真 ws 客户端打 startServer。①坏Origin 401 ②错路径 destroy ③超 maxPayload 1009 ④spawn失败 exit(-1) —— 沙箱内全绿;⑤attach→output ⑥重连回放(F5/F6,含
__MARK_测试__CJK)—— orchestrator 加PTY_AVAILABLE门控(沙箱自动 skip,真机跑),关沙箱验证 6/6 全绿。 - 验证(orchestrator 独立复核): 每步
tsc --noEmit✓ + 全量npm test递增至 187 全绿;集成 sandbox-off 6/6(真 PTY)。 - 决策 / 偏离: T12 onData 时间戳用 Date.now()(事件时刻,符合 lastOutputAt 语义);T15 由 orchestrator 加 PTY 可用性门控(原
it.skip→itPty),使真机会跑、沙箱自动跳过。 - commit:
20212dd(T12)·a2897b2(T13)·0dac54b(T14)·cdec19a(T15)
2026-06-16 · W1 前端批次(T8/T9/T10/T11)
- 状态:
[x]ALL DONE - 派活方式: 因 T11 import T10 的 mountKeybar 且验收是整目录 tsc.web,分两步:先并行 T8+T9+T10(haiku),后 T11(sonnet)。general-purpose 内联角色,共享树 + 文件不交叉。
- 改动:
- T8
public/index.html:#term/#keybar、viewport、<script type=module src=./build/main.js>+ link ./build/main.css 与 ./style.css(按 §1 约定)。 - T9
public/style.css:全屏终端、键栏 fixed 底部 flex、>768px 隐藏、深色;不碰 xterm 内部样式。 - T10
public/keybar.ts(+ test):KEY_MAP 纯数据(Esc/Shift+Tab/方向/Enter=\r/Ctrl+C/Tab)+ mountKeybar(onSend) touchstart→onSend+preventDefault。13 测试。 - T11
public/main.ts:esbuild 入口(import xterm css);xterm+FitAddon;WS scheme 随协议(M6);attach 首帧带 localStorage sessionId;onData→input、output→write、exit→ANSI 提示按回车重连;ResizeObserver 防抖 resize;指数退避重连≤30s;mountKeybar 接 ws.send;无 console.log(状态写终端)。
- T8
- 验证(orchestrator 独立复核): backend
tsc --noEmit✓;frontendtsc -p tsconfig.web.json✓;npm test6 文件 / 141 测试全绿(新增 keybar 13);npm run build:webesbuild 打包成功(main.js 430kb + main.css 6kb);build/ 已 gitignore 未入库。 - 决策 / 偏离: T11 用 1 处
@ts-ignore压 CSS import(Owns 仅 main.ts,无法另建 d.ts;记为 tech-debt);exit 后按回车 clear sessionId 再新建(契合 L1:已退出会话由服务端回放+补发 exit,前端不 reattach)。 - 遗留: 上述 css d.ts 小债;前端真实浏览器行为留 W5(T16–T20)实测。
- commit:
326d347
2026-06-16 · W1 后端批次(T3/T4/T5/T6/T7)— 5 个并行 builder
- 状态:
[x]ALL DONE - 派活方式: 5 个
general-purposesubagent 并行(自定义 module-builder agent 需重启会话才加载,故本会话内联角色), 共享工作树 + 文件所有权严格不交叉 + scopedvitest run <name>(官方文件分区法,替代 worktree;合并即 commit)。模型按 §4。 - 改动(各 agent 仅在 Owns 内):
- T3
test/helpers/mock-pty.ts(+ smoke test):IPty 替身,emitData/emitExit手动触发、记录 write/resize/kill、onData/onExit 返回 IDisposable。 - T4
src/config.ts(+ test):loadConfig(EnvLike);M1 origins 由os.networkInterfaces()推导(含 localhost,剔除 0.0.0.0/internal,http+https),wsPath 默认 /term,非法值 fail-fast,Object.freeze。 - T5
src/protocol.ts(+ test):SESSION_ID_RE(UUID v4)、parseClientMessage(永不抛,全分支校验,M7 拒abc123)、serialize。 - T6
src/session/ring-buffer.ts(+ test):M2 按真实字节(Buffer)计量、按 chunk 边界淘汰、不切碎 UTF-8/ANSI、snapshot 补\x1b[0m。 - T7
src/http/origin.ts(+ test):用 WHATWGURL比 protocol+host+port,undefined/解析失败拒绝。
- T3
- 验证(orchestrator 独立复核,非仅信 agent):
npx tsc -p tsconfig.json --noEmitPASS;npm test(全量) 5 文件 / 128 测试全绿(T4 32 + T5 60 + T6 15 + T7 12 + T3 9)。 - 决策 / 偏离: 无源码偏离。流程上:本批用 general-purpose 内联角色(见上)。T6 对"单块 > 容量"按 M2"绝不切碎"保留整块(测试固化),属忠实落地。
- 遗留 / 待办: W1 前端 T8–T11 待派;之后 T12 session(mock+ring-buffer 已就绪)。
- commit:
0fa0b69
2026-06-16 · T2 冻结共享类型契约 src/types.ts
- 状态:
[x]DONE - 改动: 新建
src/types.ts(纯类型,无实现);删src/.gitkeep。含 Config、ClientMessage/ServerMessage/ ParseResult、Dims、IPty(子集)+ IDisposable/PtyEvent、RingBuffer、WebSocketLike + WS_OPEN、SessionMeta/Session (lastOutputAt/exitedAt/exitCode)、SessionManager、MountKeybar;各模块工厂函数签名以注释列为 import 锚点。 同步更新 ARCHITECTURE §3.1/§3.4(加 wsPath、EnvLike、WebSocketLike 注记,防漂移)。 - 验证:
npx tsc -p tsconfig.json --noEmitPASS;grep 确认无ws/NodeJS/DOM 外部类型依赖(仅注释提及)。 - 决策 / 偏离 ARCHITECTURE §3(为"前后端共享 → 必须无外部类型依赖"):
loadConfig(env: EnvLike)替代NodeJS.ProcessEnv(EnvLike = Readonly<Record<string,string|undefined>>,process.env 可赋值)。Session.attachedWs: WebSocketLike替代ws的WebSocket(真实 ws 结构兼容);导出WS_OPEN=1供 M5 守卫。- 新增
Config.wsPath(默认 '/term'):不变量 8 要求 WS 路径入 config,§3.1 原 Config 漏列,已补;T4 需从 envWS_PATH读、T14 用cfg.wsPath。 三处均已回写 ARCHITECTURE,types.ts 为权威冻结源。
- 遗留 / 待办: 无。W1 全部任务现可只读 import
src/types.ts。注意:IPty/WebSocketLike 是结构子集, 实现方(T12/T14)可把真实 node-pty IPty / ws.WebSocket 直接赋值(结构兼容)。 - commit: (见下次提交)
2026-06-16 · T1 补记 — 前端打包定为 esbuild
- 状态:
[x]DONE - 改动: 装
esbuild(dev);package.json接build:web/dev:web(打包public/main.ts→public/build/main.js);.gitignore改忽略public/build/;tsconfig.web.json注释更新为"仅类型检查";前端构建约定写入PLAN §1并更新 T8/T11/T14 与DISPATCH.md、ARCHITECTURE §5。 - 验证: esbuild 0.28.1;临时入口
--bundle --format=esm --outdir=public/build出main.js+.map,smoke 通过。 - 决策: 三选一中选 esbuild(极快、零配置、与 node-pty/tsx 生态一致);现在装好避免 W1 并发 install。
约定:入口 main.ts、产物 public/build/(已 gitignore)、index.html 用
<script type=module src=./build/main.js>、server 静态托管 public/。 - commit: (见下次提交)
2026-06-15 · T0 项目记录起点(计划就绪)
- 状态:
[x]DONE - 改动: 创建
docs/PROGRESS_LOG.md、docs/PLAN.md(细粒度 T1–T21、为多 agent 并行设计);CLAUDE.md加入 plan/log 工作流约束。 - 验证: 文档自检;LOG 任务总览的 Steps 总数已与 PLAN 各任务勾选项对齐。
- 决策 / 偏离 PLAN: 采用 TypeScript(ARCHITECTURE §0);两份设计文档已多团队交叉验证并修订 12 项;计划按依赖分 W0–W5 波次,W1 含 9 个零交叉并行任务。
- 遗留 / 待办: 进入 W0 / T1(脚手架 + 装齐依赖);T1 完成后 T2 冻结
src/types.ts,随后 W1 可大规模并行。 - commit: N/A