agent-Specialization/docs/cli_ui_display_spec.md
JOJO cc9df7959a feat(cli): 新增 React/Ink CLI 端,修复全屏布局与光标定位
## 新增:基于 React/Ink 的 CLI 终端(cli/)

使用 Ink 框架构建完整的终端 UI,取代原有的纯 print 交互方式:

- App.tsx:主应用组件,管理对话状态、任务轮询、键盘输入、slash 命令
- components.tsx:Timeline、Composer、StatusLine、Picker、SlashMenu 等全部 UI 组件
- eventMapper.ts:将后端 SSE 事件流映射为 Timeline 条目,支持 thinking/tool/assistant/sub_agent 等类型
- api.ts:封装所有后端 REST 接口(任务、对话、模型、权限、工作区等)
- commands.ts:slash 命令定义(/new /resume /model /permission /execution /workspace /compact /tools 等)
- workspaces.ts:本地工作区目录的持久化管理
- types.ts:所有共享类型定义
- format.ts:context 格式化、路径缩写等工具函数

## 重大修复:去除固定高度全屏模式,恢复滚轮滚动

**问题根因**:根 Box 设置了 `height={stdout.rows}` 使 Ink 进入原地重绘模式
(类似 vim/htop),所有内容在同一块屏幕区域反复擦写,永远不会真正
"滚出"屏幕顶部,导致 terminal 的 scrollback buffer 始终为空,鼠标
滚轮无任何响应。

**修复方案**:
- 移除根 Box 的 `height` 约束和 `overflow="hidden"`
- 移除内层 Box 的 `flexGrow`/`justifyContent="flex-end"` 全屏布局
- Timeline 不再做行数截断,直接渲染全部条目
- 内容自然流入 terminal scrollback buffer,鼠标滚轮恢复正常

**同步清理**:删除 `getTimelineMaxRows`、`estimateTimelineRows`、
`scrollOffset` 状态及方向键滚动逻辑(这些都是全屏模式下的补丁,
去除固定高度后不再需要)

## 修复:Composer 光标位置偏移一行

`setCursorPosition` 中 y 轴使用 `lines.length` 导致单行时偏移 +1。
改为 `lines.length - 1`,与实际最后一行渲染位置对齐。

## 其他

- AGENTS.md / README.md:补充 CLI 启动方式与架构说明
- server/chat.py:补充任务轮询接口所需字段
- package.json:新增 cli 相关脚本入口
- docs/cli_slash_commands_spec.md、docs/cli_ui_display_spec.md:CLI 设计文档

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 01:06:51 +08:00

8.9 KiB
Raw Permalink Blame History

CLI UI 显示规范(草案)

目标:把 Web 端已有的信息表达方式,压缩成一个低噪声、可扫描、适合终端的 CLI 交互层。 适用范围CLI 前端展示层,不定义 Agent Runtime 本身。

1. 设计原则

  1. 事件流优先CLI 只消费结构化事件,不直接消费原始 API 响应。
  2. 极简优先:正文少、状态清楚、动作明确。
  3. 固定符号:统一用少量状态符号表达进度与结果。
  4. 完整展示变更:文件编辑类内容不折叠,直接完整显示。
  5. 思考可见但不冗长:只保留滚动中的短窗口,不保留完整历史。
  6. 交互独立审批、选择、Slash 命令都走独立输入态。

2. 总体布局

[历史事件区]
[当前思考 / 工具 / 子智能体输出]
[最终结果]
──
[输入栏]
[状态栏]

底部状态栏建议

5 后台智能体  3 后台指令  版本控制: 开  自动审批  沙箱  24k/200k

可扩展字段:

  • 模型名
  • 运行模式thinking / fast 先不做切换也可保留显示位)
  • Git 开关
  • 自动审批状态
  • 沙箱/直连状态
  • 上下文剩余量

3. 状态符号

状态 符号 颜色 说明
运行中 / 交替 黄/灰 闪烁
完成 绿 静态
失败 静态
用户输入 白/青 静态
审批中 慢闪
已取消 静态

4. 思考块显示

思考块只保留一种样式:5 行滚动窗口

4.1 运行中

◦ 思考中
  └ 第一行
    第二行
    第三行
    第四行
    第五行

当加入第 6 行时,自动滚动:

◦ 思考中
  └ 第二行
    第三行
    第四行
    第五行
    第六行

4.2 完成后

• 思考完成 4.2s

不做折叠展开,不保留完整思考历史。

4.3 规则

  • 只显示最新 5 行
  • 只显示当前思考内容
  • 完成后只留完成状态
  • 不显示“思考全文”

5. 子智能体前台显示

子智能体前台展示方式与思考块一致,也是 5 行滚动窗口。

◦ 子智能体 review-agent...
  └ 运行 ls -la && pwd
    搜索 python 新版本
    第三行
    第四行
    第五行

完成后:

• 子智能体完成 8.1s

规则

  • 子智能体只显示前台进度,不显示全部历史
  • 只保留最新 5 行
  • 完成态只显示耗时和结果摘要

6. 工具块通用格式

统一使用:

• 动作摘要
  └ 结果摘要

6.1 运行中

◦ 运行 npm run build
  └ 正在执行...

6.2 完成

• 运行 npm run build
  └ 成功,用时 8.2s

6.3 失败

• 运行 npm run build
  └ 失败exit code 2

7. 工具显示规范

下面定义 后面的内容包含哪些字段、如何显示。

7.1 运行命令类

适用工具

  • run_command
  • terminal_input
  • terminal_session
  • terminal_snapshot

显示字段

  • command / input
  • timeout
  • exit_code
  • stdout / output
  • stderr(若可分离)

显示规则

• 运行 printf '' > test.txt && ls -l test.txt
  └ -rw-r--r--  1 jojo  staff  0 May 14 14:48 test.txt

长输出:

• 运行 ls -la && pwd
  └ 第 1 行内容
    第 2 行内容
    … +6 lines
    第 9 行内容
    第 10 行内容

约束

  • 第一行优先显示结果中最有信息量的一行
  • 长文本可局部截断,但不要丢掉关键错误
  • 如果有 exit code必须显示

7.2 文件编辑类

适用工具

  • write_file
  • edit_file

原则

必须完整显示,不折叠。

因为:

  • 这是文件内容本身,不是工具摘要
  • 单次替换通常不会太大
  • 终端里保留完整 + / - 最有价值

7.2.1 Write

显示字段:

  • path
  • modeappend / overwrite
  • status
  • content

显示模板:

• Write test.txt (+3 -0)
    1 +这是第一行测试内容。
    2 +这是第二行测试内容。
    3 +这是第三行测试内容。

7.2.2 Edit

显示字段:

  • path
  • replace_all
  • found
  • replacements
  • 每个 replacement 的 old / new

显示模板:

• Edited main.py (+2 -2)
    14 -old line 1
    15 -old line 2
    14 +new line 1
    15 +new line 2

7.2.3 规则

  • 不折叠
  • 不显示上下文行
  • 只显示参数里已有的变更信息
  • 若存在多段替换,按替换顺序逐段输出

7.3 阅读类

适用工具

  • read_file
  • read_skill

显示字段

  • path
  • status
  • size
  • read_type
  • content
  • search / extract 子模式信息

显示模板

普通读取:

• 阅读 test.txt
  └ 读取 3 行

提取模式:

• 阅读 test.txt
  └ 提取第 12-28 行

搜索模式(如果未来恢复):

• 搜索 handle_task
  └ 找到 12 处,位于 5 个文件

规则

  • 正文尽量压缩成“读取了多少 / 提取了什么 / 找到了什么”
  • 需要看全文时再走展开命令

7.4 搜索类

当前 CLI v1 不做搜索工具

如果未来恢复,显示方式参考阅读类:

• 搜索 "xxx"
  └ 找到 N 处,位于 M 个文件

7.5 文件创建 / 删除 / 重命名

当前 CLI v1 不做这些工具块,可直接忽略。


7.6 子智能体 / 后台任务类

显示字段

  • name
  • status
  • latest_progress_lines
  • elapsed

显示模板

◦ 子智能体 review-agent...
  └ 运行 ls -la && pwd
    搜索 python 新版本
    第三行
    第四行
    第五行

完成后:

• 子智能体完成 8.1s

规则

  • 只显示最新 5 行
  • 运行中用
  • 完成后用

7.7 审批 / 交互类

适用场景

  • 权限审批
  • 需要用户确认的危险操作
  • 自定义选择
  • Slash 命令补全

显示方式

使用输入栏扩展 + 选项列表,不是普通消息流。

示例:

┌────────────────────────────────────┐
│ 需要批准:运行 rm -rf dist         │
│ 原因:删除构建目录                 │
│                                    │
│  1. Allow once                    │
│   2. Always allow                  │
│   3. Deny                          │
└────────────────────────────────────┘

规则

  • 上下键切换
  • 当前行高亮
  • Enter 确认
  • Esc 或拒绝关闭

8. Slash 命令规范

所有操作都通过 / 输入。

8.1 触发方式

输入 //a 后,打开命令建议列表。

8.2 显示方式

 /approve   简要说明
  /allow     简要说明
  /assist    简要说明

8.3 规则

  • 支持模糊过滤
  • 上下键选择
  • 当前项变色
  • Enter 执行

9. 最终输出区

任务完成后,使用固定分隔结构:

──

• 已创建 test.txt 文件。

  1. 修改文件列表test.txt
  2. 核心变更点:在 /Users/jojo/Desktop/test 下创建了一个空文本文件
  3. 验证结果:已执行 ls -l test.txt文件存在。

──

最终结果字段

建议统一输出:

  1. 修改文件列表
  2. 核心变更点
  3. 验证结果

如果有失败:

  • 失败原因
  • 已做的尝试
  • 下一步建议

10. 信息优先级

从高到低:

  1. 当前交互态(审批 / 命令选择)
  2. 当前思考 / 当前子智能体进度
  3. 当前工具输出
  4. 最终结论
  5. 低优先级元信息

11. 参考的 Web 端表现

Web 端已经存在的对应参考:

  • static/src/components/chat/actions/ThinkingAction.vue
    • 思考块
    • 运行中 / 完成两态
    • 可折叠
  • static/src/components/chat/actions/ToolAction.vue
    • 工具块
    • renderEnhancedToolResult()
    • 文件编辑 / 读取 / 命令 / 图片等不同分支
  • static/src/components/chat/StackedBlocks.vue
    • 思考 + 工具的堆叠展示
  • static/src/components/chat/MinimalBlocks.vue
    • 摘要组展示
    • 最终只保留当前步骤预览
  • static/src/components/panels/ToolApprovalPanel.vue
    • 审批面板
    • 编辑 diff、写入内容、命令预览

CLI 的目标不是复制这些视觉细节,而是抽取其中最有信息价值的字段。


12. 推荐的 CLI 默认策略

默认开启

  • 5 行思考滚动
  • 5 行子智能体滚动
  • 文件编辑完整展示
  • 命令输出摘要展示
  • Slash 命令补全
  • 审批选择弹层

默认关闭

  • 完整思考历史
  • 文件创建 / 删除 / 重命名显示
  • 搜索工具
  • 折叠展开复杂交互

13. 一句话总结

CLI 的目标是:

用最少的符号,表达最多的有效信息;用固定的状态和结构,让用户能一眼看懂 Agent 在做什么。