docs/plans/2026-07-16-webshell-git-integration.md
Goal: 把 Web Shell 从一个“只能看到分支名”的 git 盲区,建设成覆盖 状态感知 → diff review → 提交 → 分支 → GitHub 协作 → 远程同步 的完整 git 工作台,并针对“这是一个 AI agent 工具”的特殊性,优先做好 per-turn diff (review agent 这一轮改了什么)。
Architecture: daemon 是唯一的 git 权威入口(所有 git/gh 操作走 daemon REST,浏览器不直接碰仓库);core 复用既有
gitDiff.ts/gitDirect.ts/FileHistoryService等能力,不重写 git 解析;SDK 承载类型与向后兼容; Web Shell 只做展示与受控操作。Tech Stack: TypeScript、React、Node
child_process(git/gh)、Vitest、 Shiki(diff 高亮)。大 diff 靠 core 端上限 + 单文件懒加载控制,本期不引入 虚拟滚动(见设计文档"本期不引入虚拟滚动")。
第一层、第二层的接口与组件细节见设计文档
docs/design/2026-07-16-webshell-git-status-diff.md;本文档是全阶段路线图与
优化总览。
Web Shell 当前的 git 集成只有两处:
GitBranchIndicator),只显示分支名,数据来自 daemon
WorkspaceGitState(只追踪 branch)。/diff 斜杠命令,ACP 透传,daemon 走非交互路径返回纯文本统计
(diffCommand.ts)。而 core 已有大量可复用能力:
gitDirect.ts:resolveBranchName(直读 .git/HEAD)、watchRepoBranch
(监听 reflog)、readGitHead(branch / detached)。gitDiff.ts:fetchGitDiff、fetchGitDiffHunks、GitDiffResult /
PerFileStats、上限常量、isInTransientGitState。gitUtils.ts:findGitRoot、getRecentGitStatus。fileHistoryService.ts:per-turn 文件 checkpoint(turn diff 的基础)。DiffDialog.tsx:useTurnDiffs / useDiffData / TurnDiff,已实现
交互式 diff 查看。gh 已认证
时出现;transient state(merge/rebase)显式 surfaced 而非隐藏。branch chip 从“分支名”升级为“实时状态条”。
getGitWorkingTreeStatus(已实现 dirty/ahead-behind/stash/detached),
本阶段再补 detectGitOperation + conflicted,并把“transient 返回 null”
改为“返回带 operation 的状态”(git status 在 merge/rebase 期间仍可用)。WorkspaceGitState.getStatus 输出 enriched v2(已实现 dirty 部分)。DaemonWorkspaceGitStatus 加 v2 可选字段。↑N↓M / stash 角标 / detached 变色 /
operation 徽标(如 REBASING)/ 冲突数。git_branch_changed(实时);重字段按需 + focus +
git_branch_changed + 仅当前 workspace 低速轮询。详见设计文档第一层。
fetchGitDiffHunksForFile(cwd, path)(单文件按需 hunk)。GET /workspace/git/diff(文件列表 + 统计)、
GET /workspace/git/diff/file?path=(单文件 hunk)。DaemonWorkspaceGitDiff / DaemonWorkspaceGitDiffHunks 类型 +
workspaceGitDiff() / workspaceGitDiffFile(path)。GitDiffDialog(文件列表 → 点开按需加载行级 diff,Shiki 高亮;
大 diff 靠 core 上限 + 单文件懒加载控制,本期不引入虚拟滚动);/diff
本地化打开该弹窗;dirty chip 点击联动。详见设计文档第二层。
对 AI agent 工具,“agent 这一轮改了哪些行”比“工作区 vs HEAD”更精准 (后者混入用户自己的改动)。CLI 已有完整基础设施。
FileHistoryService 的 per-turn checkpoint 通过 REST 暴露
(turn 列表 + 每个 turn 的文件 diff),复用 TurnDiff / TurnFileDiff。GitDiffDialog 增加视图切换——“工作区 vs HEAD” / “本 turn 改动”;
或在每条 agent 消息旁提供“查看本次改动”入口。useTurnDiffs / useDiffData 的数据形态,避免重造。FileHistoryService),或 git checkout
单文件 / git restore。daemon 封装 gh CLI(需 workspace 内 gh 已认证),暴露 REST:
gh auth status 未认证时隐藏 GitHub UI 并引导认证。user.name / user.email。requireTrustedWorkspaceRuntime / untrusted workspace catalog)。读
(status/diff)可放开;写(commit/push)与 gh(可能泄露仓库信息)需按
workspace 信任级别门控。getRecentGitStatus),让 agent 知道当前处境。onSessionIdChange /
sidebar renderer 等 props),git 状态可通过 props/customization 暴露给宿主。git status 是子进程,大仓库慢。
git status --no-ahead-behind,ahead/behind 单独算并缓存(只在 commit /
fetch 后变)。.git/index mtime(廉价)感知 stage / commit,替代部分轮询。GIT_TIMEOUT_MS=5000)+ 可取消。getStatus / diff 请求(chip + 弹窗)合并为一次 git
调用(in-flight coalescing)。git_branch_changed /
focus / index 变化时失效。virtual-viewport)已明确不在本期范围,留待
后续。computedAt 比较),避免旧响应覆盖新结果。gh(有限速)。path 查询参数在 core + daemon 两层校验(拒绝绝对
路径 / .. 越界段),最终由 git 限定在仓库内。sanitizeFilenameForDisplay 语义)。gh / git 一律用 execFile 数组参数,不拼字符串。gh 认证后出现。i18n.tsx)。↑↓ 数字、
conflict 用 ! 角标),符合 WCAG 对比度。git init -b(2.28+)、
--porcelain=v2(2.11+)等;本阶段用 --porcelain=v1(已确认);新特性先
探测 git --version 或优雅回退。gh 可执行名
差异;子进程用 execFile 数组参数避免 shell 差异。GIT_TIMEOUT_MS);
网络型 fetch/pull/push/gh 用更长且可配置超时,并区分“慢”与“挂死”。core (gitDirect + gitDiff + FileHistoryService)
→ daemon (WorkspaceGitState / git routes / gh wrapper) ← 唯一 git 权威
→ SDK (DaemonClient + types + events,向后兼容)
→ webui (mappers: 事件 → connection 状态)
→ Web Shell (chip / GitDiffDialog / 各 dialog)
git_branch_changed(实时、廉价);重字段走 REST
workspaceGit()(按需 + 触发式)。gh wrapper + REST,能力门控。Phase 1 状态感知 + operation 指示 ← 当前(core/daemon 已完成大半)
Phase 2 可视化 diff(工作区 vs HEAD)
Phase 3 per-turn diff ★ 优先于 GitHub
Phase 4 提交工作流 + 丢弃改动
Phase 5 分支管理
Phase 6 GitHub 集成 ★ 后续重点
Phase 7 远程同步
gh 认证),可在 Phase 3 之后并行推进。requireTrustedWorkspaceRuntime);读操作(status/diff)可放开。这应在对应
阶段同步实现,而非事后补。feat/webshell-git-status-chip)getGitWorkingTreeStatus + parseStatusBranchLine /
parseStatusEntries + detectGitOperation + conflicted,transient surfaced
WorkspaceGitState.getStatus 输出 enriched v2,透传
operation / conflicted + 单测(14 个通过)。DaemonWorkspaceGitStatus v2 字段(v: 1 | 2)+ DaemonGitOperation。↑N↓M / stash / detached 换图标 /
operation 徽标 / 冲突数,非颜色兜底)+ 刷新策略(focus + branch 变化 +
仅当前 workspace 30s 可见性轮询)+ i18n + 单测(GitBranchIndicator 8 +
ChatEditor&App 118)。feat/webshell-git-status-chip)可视化 diff(工作区 vs HEAD):「Changes」弹窗 = 文件列表 + 单文件 hunk 懒加载
/diff 本地拦截打开弹窗;dirty chip 点击联动。fetchGitDiffHunksForFile + toRepoRelativePath +
synthesizeUntrackedHunk(untracked 全新增、删除文件可 diff、O_NOFOLLOW、
二进制/上限)+ 单测(gitDiff.test.ts 88 通过)。GET /workspace/git/diff(列表+统计)与 .../diff/file?path=
(单文件 hunk)+ qualified 版本 + server 注册 + 单测(8 通过)。
单文件路由不经 fs factory('read' 拒绝已删除文件),改由 trust gate +
core repo-relative 规范化 + git 仓库内含 + O_NOFOLLOW + ls-files gate。DaemonWorkspaceGitDiff / ...File / ...Hunks / DaemonDiffHunk
workspaceGitDiff() / workspaceGitDiffFile(path)(两类 client)+
bundle 上限 160→165KB。GitDiffDialog(DialogShell + per-side codeToTokens 高亮 +
懒加载 + 占位态)+ GitBranchIndicator 可点击 chip + ChatEditor 透传 +
App.tsx 状态/渲染//diff 拦截 + localCommands 补全 + i18n(en/zh-CN)
GitDiffDialog 5 + GitBranchIndicator 9)。详细 task 分解见 docs/design/2026-07-16-webshell-git-status-diff.md 的
「Phase 2 详细实施计划」。
feat/webshell-git-status-chip)侧栏 workspace git 下放:左侧每个 workspace 文件夹行显示一个 git 图标 + 状态点
(dirty/冲突/进行中),分支名 + ahead/behind 收于 hover tooltip;点击直接打开
该 workspace 的 Changes 弹窗(复用 Phase 2 的 GitDiffDialog)。多仓库 git
状态无需逐个切换即可一览。
WorkspaceSection:trusted(且传入 onOpenGitDiff)时拉 workspaceGit()
(mount/focus/reloadToken/可见时 60s 慢轮询)+ header 行渲染
GitBranchIndicator(兄弟节点,规避按钮嵌套,传 compact 走图标态)+
.gitPill 借 :has() 让图标紧贴名称、操作贴右缘。App.tsx:弹窗状态 showGitDiffDialog: boolean → diffWorkspaceCwd: string | undefined 重构;工具栏 chip 与侧栏 chip 统一经 setDiffWorkspaceCwd
打开同一弹窗、各指向自身仓库。WebShellSidebar → WorkspaceSection。git.*(无新键)+ 新增 WorkspaceSection.test.tsx(4 通过)。详细决策见 docs/design/2026-07-16-webshell-git-status-diff.md 的
「Phase 2.5」节。
--no-ahead-behind + index watch + 仅活跃 workspace
轮询 + 超时。FileHistoryService,需确认
daemon 侧能拿到 session 的 checkpoint 数据。gh 未认证时优雅降级并引导。