Skip to content

Wave Code CLI ​

基于 React Ink 构建的 CLI 终端界面,提供交互式 AI 编程助手体验。


1. 安装与启动 ​

1.1 安装 ​

bash
npm install wave-code -g

1.2 运行模式 ​

Wave CLI 提供三种运行模式,适用于不同场景:

交互模式(默认)

启动基于 React Ink 的终端 UI,支持实时对话、流式输出和完整的交互体验。

bash
wave

打印模式(--print / -p)

非交互式运行,接收输入并一次性输出结果,适用于脚本集成和自动化流水线。

bash
wave -p "解释这个项目的架构"
echo "分析这段代码的问题" | wave -p

配合 --show-stats 可在输出末尾显示耗时和 Token 用量统计;配合 --output-format json / stream-json 可输出机器可读结果(见 结构化输出)。


2. 命令行选项 ​

2.1 会话控制 ​

选项简写描述
--restore <id>-r按 session ID 恢复会话;不指定 ID 则列出可用会话
--continue-c自动继续上次会话
bash
# 列出可恢复的会话
wave -r

# 恢复指定会话
wave -r <session-id>

# 继续上次会话
wave -c

2.2 模型与工具 ​

选项描述
--model <name>指定 AI 模型
--tools <list>启用的工具列表(逗号分隔)
--allowed-tools <list>始终允许的工具列表
--disallowed-tools <list>始终禁用的工具列表
--mcp-config <json>MCP 服务器配置(JSON 字符串)
bash
# 指定模型并限制工具
wave --model gpt-4o --disallowed-tools Bash,Write

2.3 权限与安全 ​

选项描述
--permission-mode <mode>设置权限模式:default、acceptEdits、bypassPermissions、dontAsk、plan
--dangerously-skip-permissions跳过所有权限检查(危险)
--add-dir <path>将目录加入会话安全区域,可重复指定(仅当前会话生效)
bash
# 将 /data/exports 加入当前会话的安全区域
wave --add-dir /data/exports

2.4 工作目录 ​

选项简写描述
--worktree [name]-w在 git worktree 中启动,可选指定名称
bash
# 自动命名 worktree
wave -w

# 指定 worktree 名称
wave -w my-feature

2.5 其他 ​

选项简写描述
--plugin-dir <path>从指定目录加载插件
--show-stats打印模式下显示耗时和 Token 统计
--output-format <fmt>打印模式输出格式:text(默认)、json、stream-json
--version-v显示版本号
--help-h显示帮助信息

3. 子命令 ​

3.1 插件管理 ​

bash
# 市场管理
wave plugin marketplace add <input>          # 添加插件市场
wave plugin marketplace update [name]        # 更新已注册的市场
wave plugin marketplace list                 # 列出所有已注册市场

# 插件操作
wave plugin install <plugin>                 # 从市场安装插件
wave plugin list                             # 列出市场中可用插件
wave plugin uninstall <plugin>               # 卸载插件
wave plugin update <plugin>                  # 更新插件(卸载后重新安装)

安装插件时支持指定作用域(不指定时默认 user 作用域,安装即启用):

bash
wave plugin install my-plugin@official --scope user     # 全局安装(默认)
wave plugin install my-plugin@official --scope project  # 项目级安装
wave plugin install my-plugin@official --scope local    # 本地安装

3.2 更新 ​

bash
wave update    # 更新 Wave CLI 到最新版本

Windows 上更新时提示「更新将在后台完成」并立即退出当前进程,由分离的后台子进程完成安装,避免更新程序自身占用 bin 文件句柄导致安装失败(EPERM/EBUSY)。

版本查询与安装都走 npm 国内镜像(registry.npmmirror.com),避免直连官方源时更新缓慢或超时;镜像不可达时自动回退到官方源,两个源都连不上才会报错退出。手工安装同理,可自行加 --registry。

3.3 Daemon 客户端命令 ​

wave --daemon <socket> 以守护进程方式启动 Wave,托管后台 agent 会话(远端主机上由桌面端经 SSH 隧道访问;本机则是内置 wave-daemon 技能把长任务委派给它)。与之对应,wave daemon <子命令> 是访问该 daemon 的客户端命令组,可在本机或远端主机上(或经 ssh <host> wave daemon ...)查看、续聊与审批 daemon 托管的会话,无需打开完整 UI。所有子命令非交互式运行:结果输出到 stdout、诊断输出到 stderr,便于脚本与管道消费。

bash
# 列出 daemon 当前托管(进程内存中 live)的全部会话:会话 ID、工作目录、状态、消息数
wave daemon list

# 查看指定会话的实时状态(生成中/空闲/等待审批)与最近消息(默认只展示最后 1 条)
wave daemon status <sessionId> [--lines 1]

# 阻塞盯住指定会话,等它空闲(或挂起等待审批)就退出,退出时打印最终快照
# 退出码:0=等到空闲 / 3=挂起等待审批(立刻返回)/ 1=错误(连不上、会话不存在或在等待期间被销毁、--timeout 到点)
wave daemon wait <sessionId> [--lines 1] [--from-busy] [--timeout 600]

# 向会话注入一条消息(默认异步派单:发完即退,不等待回复;进度用 status 查看)
wave daemon send <sessionId> "继续"
# 需要同步等待回复时传 --wait <秒>:等待该消息对应的回复完成后输出助手最终回复
wave daemon send <sessionId> "继续" --wait 600

# 处理会话挂起的权限请求:允许 / 拒绝(可附原因)
wave daemon respond <sessionId> <requestId> --allow
wave daemon respond <sessionId> <requestId> --deny --reason "原因"

# 中断会话正在生成的回复(含子代理、bash 命令与排队消息)
wave daemon abort <sessionId>

# 优雅关闭 daemon(各会话存盘收尾后进程退出;未运行时幂等成功)
wave daemon stop

# 重启 daemon(CLI 升级后使用:先优雅停旧进程,再拉起当前 CLI 的新 daemon)
wave daemon restart

要点:

  • 所有子命令固定连接默认 socket(~/.wave/daemon.sock;Windows 上同一地址是命名管道 \\.\pipe\wave-daemon-<hash>,由客户端与服务端各自从该路径推导,无需也无法手工指定),不提供 --socket 覆盖参数
  • 语义区分:wave --daemon <socket> 是启动 daemon(服务端),wave daemon <子命令> 是访问 daemon(客户端),两者互不干扰
  • daemon 一经拉起即常驻运行(空闲不退出),仅在被 kill / 升级重启 / 机器重启后消失;daemon 未运行时,任一子命令自动以后台进程方式拉起 daemon 并重试连接,仅当拉起的 daemon 在启动超时内未就绪时才以非零退出码报错退出,不进入 TUI、不挂起。拉起失败时错误信息带上 daemon 的退出码与最后一行 stderr(daemon 的 stderr 追加写入 ~/.wave/logs/daemon.log,可用 WAVE_LOGS_DIR 改目录)——不会只剩一个 ENOENT 让用户猜
  • wave daemon list 仅展示当前 daemon 进程内存中 live 的会话(不扫磁盘索引);知道 sessionId 时即使不在列表中,也可经 status / send 重新载入
  • wave daemon status 默认只展示最后 1 条消息(消息正文完整不截断,默认值保持输出有界);--lines N 展示最近 N 条,--lines 0 只输出 session 头与 Status: 行。status 始终是「取一次快照、立刻返回」的契约;需要阻塞等到状态变化(生成中 → 空闲,或挂起审批)时用 wave daemon wait。Status: 与 wait 用同一空闲判据(无回合、无后台任务/子 agent、无排队消息):前台回合已结束但后台任务/子 agent 仍在跑的会话显示 generating,不会出现「status 说 idle、wait 却不返回」的矛盾取值
  • wave daemon wait 是「盯会话」的正式入口:订阅 daemon 推送的 loadingChange 与 backgroundTasksChange(后台任务起止)作唤醒、醒来读注册表判空闲(不轮询),退出时按 wave daemon status <id> --lines N 的同格式把最终快照打到 stdout(msg=$(wave daemon wait <id>) 拿到的就是汇报本身),进度提示走 stderr;退出码为契约——0=等到空闲、3=会话挂起等待权限审批(立刻返回并打印待审批清单,respond 后可重新 wait)、1=错误(daemon 连不上 / sessionId 不存在或在等待期间被销毁 / --timeout 到点)。「空闲」的判据与 wave -p(print mode)一致:无进行中的回合、无运行中的后台任务/子 agent、无待处理消息,三项同时成立——只结束了前台回合、后台任务/子 agent 还在跑的会话不算空闲(这是刻意为之:否则会在后台任务还没跑完时就假报「已完成」),因此挂着长跑后台任务的会话(如长期跑着的 dev server)永远不会空闲、必须显式传 --timeout。调用时已空闲则立即退出 0;--from-busy 要求先拿到「那一轮忙确实发生过」的证据才判空闲(消除紧跟异步 send 之后立刻 wait 的 stale 快照竞态,运行中的后台任务同样算「忙」)——证据可以是观察到过本会话的非空闲,也可以是忙已经结束:注册表读到空闲时本会话转录里最新一条消息是 assistant(回复已产生),这正是 wave daemon send <id> "…" → 等回合跑完 → wave daemon wait <id> --from-busy 这种取汇报的写法(这种情况下忙与 waiter 从不重叠,只认「观察到忙」会一直等到 --timeout,默认即永久挂起);反之最新一条是 user(回复还没出现)或会话没有任何消息时继续等;--timeout <秒> 默认无限等待
  • status / wait 的推送按 sessionId 分路:daemon 把每条会话级通知广播给所有已连接客户端(信封标注 sessionId,一个连接可承载多个会话,如桌面端远端),因此命令订阅后先按本次 attach 的 sessionId 过滤再写状态——别的会话的 loadingChange / userMessageAdded / assistantMessageAdded 不得翻转 Status:、不得结束等待、也不得替 --from-busy 满足「忙」
  • 无人值守监控不需要外层 shell 轮询脚本:wave daemon wait 的三种终止已覆盖全部终止情形——0=目标会话完成(stdout 即最终快照)、3=卡在审批(去 respond 后续等)、1=出错(含等待期间该会话被其它客户端 destroy:命令会按「会话已不存在」明确报错退出,绝不挂住、也绝不把已消失的会话误报成「已完成」;daemon 侧保证销毁是「先摘注册表条目、再做耗时收尾」,因此整个销毁窗口内该会话都已按「不存在」对待)。因此调用方直接 msg=$(wave daemon wait <id>) 取汇报、按退出码分支即可;权限审批与会话是否仍存活这两类事实施信不了推送(会话被销毁只伴随一次 loadingChange:false,与「本轮生成结束」同形),由同一个 2 秒兜底查询覆盖(不是每 N 秒查一次 status 的状态轮询)
  • 换键与就地重建不等于销毁:会话在存活期间可以换 sessionId(清空对话铸出新 id)——daemon 会广播新 id,wait / send --wait 跟着重绑到同一个会话(不因旧 id 消失而报退出码 1 / 假超时);配置保存触发的就地重建期间会话仍在注册表中并被报为「生成中」(绝不出现「条目在 + 未生成」这个会被误判成空闲的组合),读类请求照常、写类请求以可重试错误拒绝,重建完成后照常判定,只有重建失败才会话消失(走「已不存在」退出码 1)
  • wave daemon send 默认异步派单:注入消息后立即退出码 0(stdout 输出 Sent message to session: <sessionId> 确认,不等待回复、不输出回复文本),消息照常在 daemon 中处理,进度用 wave daemon status 查看;需要同步等待回复时传 --wait <秒>(如 --wait 600,等待超过 N 秒无回复即以非零退出码退出;会话挂起等待审批时超时退出,提示先经 wave daemon respond 处理)
  • wave daemon respond 按工具智能补全决策:EnterPlanMode 的 --allow 自动附带 plan 模式切换;AskUserQuestion 需用 --answer '{"问题":"答案"}' 提供答案;--rule "Bash(ls)" 持久化允许规则(后续同类调用不再询问);--mode acceptEdits 切换会话权限模式
  • wave daemon abort 中断指定会话正在生成的回复(含子代理、bash 命令与排队消息),不清除已完成的对话历史;对空闲会话是幂等 no-op(仍成功退出);sessionId 不存在时以非零退出码报错;attach 是短暂访问、随用随断,中断后会话在 daemon 中继续存活
  • wave daemon stop 优雅关闭 daemon(非强杀):先销毁全部托管会话(各自存盘收尾)再退出;daemon 未运行时幂等成功(不自动拉起);wave daemon restart 先优雅停掉旧 daemon、再以当前 CLI 拉起新 daemon(未运行时等价于直接拉起)——CLI 升级后运行它让 daemon 跑新代码

4. 斜杠命令 ​

在交互模式中,输入 / 可触发命令选择器,快速调用以下内置命令:

命令描述
/help显示帮助和快捷键
/status显示 Agent 状态和配置信息
/model切换 AI 模型
/tasks管理后台任务
/mcp管理 MCP 服务器连接
/plugin管理插件
/reload-plugins就地重新加载插件(不重启会话)
/workflows查看和管理工作流运行
/rewind回滚到历史检查点
/resume恢复历史对话(会话内切换,无需退出进程)
/rename给当前对话设自定义标题
/loginSSO 企业认证登录
/logout清除 SSO 认证
/clear清除当前对话历史
/compact压缩对话历史,减少 Token 占用
/add-dir将目录加入会话安全区域(可带 --remember 持久化)
/agents查看当前会话可见的所有 agent(子代理)定义,按来源分组展示
/skills查看当前会话可见的所有技能,按来源分组展示并可查看详情
/hooks查看当前生效的钩子配置
/plan进入计划模式,或查看当前会话的计划文件
/btw旁路提问,不调用工具的快速问答

了解更多:详见功能规格说明:斜杠命令


5. 键盘快捷键 ​

5.1 输入与导航 ​

快捷键功能
Enter发送消息 / 确认选择
Ctrl+J输入换行(多行输入)
↑ / ↓浏览输入历史 / 选择器导航
@触发文件选择器,将文件或子代理添加到上下文
/触发命令选择器
!Shell 命令前缀(如 !ls -la)
Ctrl+R搜索 Prompt 历史(会话选择器内为给该会话改名)
Ctrl+V / Alt+V(Windows)粘贴剪贴板图片
Ctrl+A光标移到行首
Ctrl+E光标移到行尾
Ctrl+U删除光标前到行首的内容
Ctrl+K删除光标后到行尾的内容
Ctrl+W删除光标前一个词

占位符整块删除: 粘贴长文本或图片会生成 [LongText#N] / [Image #N] 占位符。当光标位于占位符末尾且其后为空白或行尾时,按 Backspace 会整块删除占位符(连同对应的长文本/图片附件),不会留下残缺片段;Ctrl+U/Ctrl+K/Ctrl+W 等行编辑键仍按普通字符串处理。

5.2 视图控制 ​

快捷键功能
Ctrl+O展开/折叠消息
Ctrl+T切换任务列表显示
Ctrl+B将当前任务放到后台执行

5.3 权限与确认 ​

快捷键功能
Shift+Tab循环切换权限模式
Tab在确认对话框中切换选项
PgUp/PgDn确认详情超高时翻页滚动内容区
Ctrl+U/Ctrl+D确认详情超高时上/下滚半页
Esc中断 AI 响应 / 取消选择器 / 关闭帮助
Esc ×2空闲时双击清空输入(并保存到历史)

确认详情(超长 plan 或大 diff)超高时,内容区可独立滚动(PgUp/PgDn 翻页、Ctrl+U/Ctrl+D 半页),选项列表固定底部始终可见,底部显示滚动快捷键提示(内容不超高时不显示)。


6. 权限模式 ​

Wave 提供五种权限管理模式,控制 AI 调用工具时的确认行为:

模式描述
default受限工具需要用户确认,最安全的模式
acceptEdits自动接受文件编辑操作,其他工具仍需确认
bypassPermissions自动接受所有工具调用,无需任何确认(危险)
plan计划模式,AI 只能修改计划文件,适合项目规划阶段
dontAsk自动拒绝受限工具,AI 不会请求确认也不会执行

切换方式:

  • 交互模式中按 Shift+Tab 循环切换
  • 启动时通过 --permission-mode 指定
  • 使用 --dangerously-skip-permissions 等同于 bypassPermissions

默认自动放行的只读命令:

default 模式下,以下只读 git 命令默认直接执行、不触发权限确认:git status、git diff、git log、git show、git branch(含 --list / -a / -r / -v 等变体)、git tag、git remote、git ls-files、git rev-parse、git config --list、git cat-file、git count-objects。命令带全局作用域参数(如 git -C <path> status、git --work-tree <path> diff)时同样匹配自动放行;写操作(git push、git commit、git branch -D 等)不受影响,仍按正常权限流程确认。


7. 特色功能 ​

7.1 Bash 模式 ​

在输入框中以 ! 开头直接执行 Shell 命令,无需离开聊天界面。

bash
!ls -la
!git status
!npm test

命令以 user 消息 + bash tool block 形态显示在消息流中,输出在命令结束后一次性显示、全量展示;失败时在输出前标注 [exit code: N]。长时间运行的命令支持随时中止。

7.2 BTW 旁路提问 ​

/btw <question> 向 AI 快速提问,AI 不会调用任何工具,仅基于已有上下文直接回答。适合快速确认思路或获取解释,不产生工具调用开销。

/btw 这个函数的时间复杂度是多少?

回答期间显示 ✻ Answering 加载提示,其后实时展示当前流式文本的最后 30 个字符(超出截断、换行折叠),按 Esc 可中止请求。

回答较长时显示在固定高度的视口中,用 ↑/↓(或 Ctrl+P/Ctrl+N)每次滚动 3 行,按 Esc 关闭。

7.3 Git Worktree ​

通过 --worktree 在隔离的 git worktree 中启动,安全实验新功能而不影响主分支。

bash
wave -w my-feature

在交互模式中也可通过内置工具 EnterWorktree 切换到 worktree。

在退出对话框选择 "Remove worktree"(或打印模式 -p 正常退出清理、WorktreeRemove hook 接管)删除 worktree 时,会显示 Deleting worktree ... 进度提示,完成后显示 Done.;删除失败显示错误信息而非完成提示。选择 "Keep worktree" 保留时无提示。

7.4 Compact 压缩 ​

/compact 压缩当前对话历史,将冗长的上下文总结为精简摘要,减少后续请求的 Token 占用。支持附加自定义指令引导压缩方向。

/compact 重点保留 API 设计相关的讨论

压缩进行中消息列表下方显示 ✻ Compacting 提示,其后实时展示当前流式文本的最后 30 个字符(超出截断、换行折叠),压缩完成后提示消失。

7.5 Rewind 回滚 ​

/rewind 将对话回滚到历史检查点,撤销后续的对话记录和文件更改。

7.6 图片粘贴 ​

按 Ctrl+V 粘贴剪贴板中的图片,支持跨平台(macOS、Linux、Windows)。AI 可识别截图中的 UI 设计、错误信息或架构图。Windows 终端将 Ctrl+V 保留为系统文本粘贴,按键不会到达 CLI,因此 Windows 下使用 Alt+V(与 Claude Code 一致)。

7.7 MCP 集成 ​

通过 /mcp 管理 MCP(Model Context Protocol)服务器连接,扩展 AI 的外部工具能力。支持在项目根目录的 .mcp.json 中配置,或通过 --mcp-config 命令行传入。

7.8 插件系统 ​

通过插件扩展 AI 的 Skill 和命令。支持插件市场的发现、安装和管理,插件可在 user、project、local 三种作用域下激活。

详见 第 3.1 节 插件管理。

7.9 Workflow 工作流 ​

通过 /workflows 查看和管理正在运行的工作流。工作流支持多阶段编排、并行执行和确定性控制流。

7.10 后台任务 ​

通过 /tasks 查看后台任务列表,或通过 Ctrl+B 将当前前台任务放到后台执行。支持 shell 命令和子代理两种任务类型,任务完成后自动通知。

7.11 SSO 认证 ​

通过 /login 进行企业 SSO 认证,授权码通过 localhost 回调自动交换为 JWT。登录后 API 请求自动通过 Wave AI 服务端代理路由,无需手动配置 API Key。通过 /logout 清除认证状态。

7.12 会话管理 ​

支持多会话的创建、恢复和管理:

bash
wave              # 启动新会话
wave -c           # 继续上次会话
wave -r           # 列出可恢复的会话
wave -r <id>      # 恢复指定会话

会话文件的元数据头持久化真实的创建时间、工作目录与 git 分支;wave -r 选择器中会话行尾显示 git 分支标签(如 [main]),多 worktree 场景下可区分同仓库不同分支的会话。

7.13 附加工作目录 ​

默认情况下,Agent 只能在当前工作目录内读写文件。通过附加目录(additional working directories)将安全区域扩展到工作目录之外,目录内的文件操作不再触发权限确认,并在系统提示词中列出。

bash
# 启动时加入(仅当前会话生效,可重复指定)
wave --add-dir /data/exports

# 会话进行中加入(--remember 追加到 .wave/settings.local.json 的
# permissions.additionalDirectories,后续会话自动加载)
/add-dir /data/exports
/add-dir --remember /data/exports

无参数执行 /add-dir 显示用法及当前会话的附加目录列表。此功能为 CLI 专属入口;配置键 permissions.additionalDirectories 为通用配置,各端(CLI、VS Code 等)均生效。

7.14 Token 用量统计 ​

在打印模式下配合 --show-stats 使用,输出结果末尾显示耗时和 Token 用量统计信息。

bash
wave -p --show-stats "分析这个项目的依赖关系"

7.15 会话内热切换 ​

/resume 在 TUI 内打开会话选择器,切换到另一段对话,不必退出进程重开:

/resume

选择器与 wave -r 共用同一组件与同一套会话归属判定(↑ ↓ 选择、Enter 确认、Esc 取消):

  • 范围:默认列出当前目录的会话;Ctrl+W 展开同仓库的全部 worktree(含主仓库),Ctrl+A 展开全部项目——展开时行尾显示会话所属项目路径。
  • 原地恢复:选中当前目录的会话即原地恢复,工作目录不变。
  • 自动切换工作目录:选中同仓库其它 worktree 的会话时直接恢复,并把会话转录文件、项目规则与记忆改按该目录解析。
  • 跨项目:选中其它项目的会话时不在当前进程恢复,选择器内显示可复制的 cd <路径> && wave --restore <sessionId>(同时写入剪贴板),按 Esc 返回——关闭提示后原会话与内容不受影响。目标 worktree 目录已被删除时按同样方式处理。
  • 运行中:正在生成回复时按 /resume 提示 Cannot resume a conversation while the agent is running.(Esc 返回),不中断当前回合。
  • 切换后:当前会话先自动存盘,消息流、Token 用量与任务列表整体换成目标会话的状态,输入框草稿清空。
  • 失败:目标会话记录不存在或损坏时不切换,提示失败原因,当前会话保持可用。

切换会触发 SessionEnd / SessionStart 钩子(来源为 resume),与 wave -r 启动时一致。

7.16 会话重命名 ​

给当前对话设一个自定义标题,替代默认的「首条用户消息截断」显示:

/rename 修复登录页样式

不带标题执行 /rename 只打印用法(Usage: /rename <title>),成功时回显 Conversation renamed to "…".。

会话选择器(/resume 与 wave -r 共用同一组件)里也能就地改名:↑ / ↓ 选中某行后按 Ctrl+R,该行标题变成输入框并全选原标题,Enter 保存、Esc 取消——选择器内 Ctrl+R 是重命名,与输入框里的 Ctrl+R(搜索 Prompt 历史)互不冲突。

  • 显示优先级:自定义标题 > 首条用户消息截断(30 字符)> 新对话;已设的自定义标题不会被后续消息按「首条消息」重新推导覆盖。
  • 存储与一致性:标题以 custom-title 保留条目追加在会话自己的 JSONL 文件里(唯一权威来源),因此 wave -r、插件端与桌面端读到的都是同一个标题。
  • 空标题:trim 后为空时只提示用法、不作改动(没有「清除标题」语义)。
  • 失败:写盘失败时报错并保持原标题不变(不静默)。

7.17 结构化输出 ​

打印模式默认输出给人看的纯文本。要让脚本或 CI 消费结果,用 --output-format 换成结构化格式,stdout 上就只剩 JSON:

bash
# 取助手回复的纯文本
wave -p "列出当前目录" --output-format json | jq -r .result

# 逐事件消费(打印进度、审计工具调用)
wave -p "解释这个项目的架构" --output-format stream-json | jq -c .

# 在流水线里按结果决定成败
wave -p "检查构建脚本" --output-format json > result.json || echo "运行失败"
jq -e '.subtype == "success"' result.json > /dev/null

--output-format json:一次运行结束时输出一条 JSON 对象。

字段类型说明
typestring固定为 "result"
subtypestring"success" 或 "error_during_execution"
is_errorboolean与 subtype 同义,便于布尔判断
resultstring最后一条助手消息的纯文本;运行失败时没有这个字段
session_idstring会话 ID,可直接 wave -r <session_id> 恢复
num_turnsnumber主 agent 的助手消息条数(流式降级的半截消息不计入)
duration_msnumber本次运行耗时(毫秒)
usageobjectprompt_tokens / completion_tokens / total_tokens,有缓存时带 cache_read_input_tokens / cache_creation_input_tokens
model_usageobject按模型分组的同一组计数,外加 operations.agent_calls / operations.compactions
tool_callsarray{id, name, input, success, error?},按调用顺序;不含工具输出(完整输出在会话记录里)
errorsstring[]仅在非空时出现:运行中的错误块(模型请求失败、hook 报错等)

--output-format stream-json:每行一个 JSON 事件。首行是 system/init(建 agent 阶段就产生的事件会被押后到它之后;若建 agent 直接失败则没有 init,流从 error 到 result),末行是与上面结构相同的 result,中间按发生顺序是:

type / subtype主要字段
system / initsession_id、model、cwd、permission_mode、tools
assistant / reasoning_deltamessage_id、delta
assistant / content_deltamessage_id、delta
tool / startmessage_id、tool_use_id、name、input
tool / endmessage_id、tool_use_id、name、success、error
errormessage
result同 json 格式的 result 对象

assistant 事件的 delta 是增量片段,拼接起来才是完整文本。

几条使用约定:

  • 退出码:0 表示这次运行干成了活。以下两类判为失败、退出码 1,此时 stdout 上仍有一条 subtype 为 error_during_execution 的 result(不带 result 字段),失败原因在 errors 里:① 建 agent 或发消息抛错;② 运行结束时错误块非空且一句正文都没产出(典型来源是模型不可用、鉴权被拒)。工具执行失败不算运行失败(记在 tool_calls 条目的 success 上),压缩历史失败、hook 报错这类可恢复错误也不算。
  • stdout 只有 JSON:--show-stats 的统计、worktree 提示等诊断信息都写到 stderr,stdout 保持纯净。万一有非 JSON 内容漏到 stdout,会被改写到 stderr 并加上 [stdout-guard] 前缀,不打断管道。
  • 只对打印模式生效:不带 -p 给非 text 的 --output-format 会报错退出(交互模式的 stdout 是终端 UI)。
  • usage 是 OpenAI 口径:prompt_tokens 已包含缓存读取的 token(与 Anthropic 的 input_tokens 口径不同),也不输出费用字段。

8. 环境变量 ​

变量默认值描述
LOG_LEVELINFO日志级别:DEBUG、INFO、WARN、ERROR
LOG_KEYWORDS-日志关键词过滤,仅输出包含指定关键词的日志
LOG_FILE~/.wave/logs/cli.log日志文件路径(桌面端/插件端分别为 desktop.log/vscode.log/jetbrains.log)

9. CI 集成 ​

把 Wave 跑进流水线:PR 提上来,job 装 CLI、执行一段提示词(例如 /code-review)、把结论写回 PR,并用退出码决定这个 check 是红还是绿。

整条链路仍然只是「在无人值守的机器上跑 CLI」:认证(三个环境变量)、权限预设(不卡在确认框)、结构化结果(wave-result.json)。命令行参数的含义见 2. 命令行选项 与 7.17 结构化输出。

9.1 跑起来是什么样 ​

GitHub 仓库加一个文件 .github/workflows/review.yml:

yaml
name: Review

on:
  pull_request:

jobs:
  review:
    uses: netease-lcap/wave-agent/.github/workflows/wave-review.yml@main
    secrets: inherit
    permissions:
      contents: read
      pull-requests: write
    with:
      prompt: /code-review

secrets: inherit 要求仓库里已经有 WAVE_API_KEY(Settings → Secrets and variables → Actions)。

提一个 PR 后会发生三件事:

  1. 内置的 /code-review 技能读本次改动,结论以 PR 评论的形式发出来;
  2. job 的 Summary 里有一段「跑成什么样」的摘要,wave-result.json 作为 artifact 保留;
  3. 运行失败时 job 变红(退出码 1),成功时变绿。

9.2 认证 ​

CI 里不用登录态,三个环境变量就够:

变量必填说明
WAVE_API_KEY是模型服务的 API Key
WAVE_MODEL是模型名,如 deepseek-flash。CI 里没有本地配置可继承
WAVE_BASE_URL否模型网关地址,留空则用 CLI 默认

三个变量都会进入 SDK 的配置解析链,与本地 ~/.wave/settings.json 里的同名键等价;少给 WAVE_MODEL 时 CLI 会直接报 Agent configuration requires model。

放哪里:WAVE_API_KEY 是密钥,放平台的 secret 机制(GitHub:Settings → Secrets and variables → Actions → Secrets);WAVE_MODEL 与 WAVE_BASE_URL 不是密钥,放仓库变量(同页 Variables)更省事,团队里一眼能看到用的是哪个模型。两者都不要写进仓库文件——wave-result.json 与 job 日志里都不含它们。

9.3 权限预设 ​

CI 里没有人在旁边点「允许」,所以要用一套不需要交互就能跑完的权限组合。三个参数分工明确:

参数作用CI 里怎么给
--permission-mode dontAsk未命中预批规则的受限工具直接拒绝,不等待输入恒定用它
--tools只注册列表里的工具,其它工具模型根本看不见按任务收窄,见下表
--allowed-tools预批规则,如 Bash(git status*)只放开确实要跑的命令

三种常见任务的最小组合:

任务--tools--allowed-tools
只读审查Read,Glob,Grep,BashBash(git diff*),Bash(git log*)
还要跑测试上面 + 你的项目工具上面 + Bash(pnpm test*),Bash(pnpm -F * test*)
允许改文件并提交上面 + Edit,Write上面 + Bash(git commit*),Bash(git push*)

dontAsk 下被拒的工具不是报错退出:拒绝原因作为一次工具结果回到模型,模型会换路子继续完成任务。技能自己声明的 allowed-tools(/code-review 这类内置技能都带)在执行该技能时也会自动注册成临时规则,不必在 allowed-tools 里重复。

9.4 GitHub Actions:用现成的可复用 workflow ​

uses: netease-lcap/wave-agent/.github/workflows/wave-review.yml@<ref>,<ref> 建议写 tag 或 commit,别跟 main。输入与输出:

输入默认值说明
prompt必填传给 wave -p 的提示词,如 /code-review
wave-versionlatestwave-code 版本;生产请 pin 具体版本
output-formatjsontext / json / stream-json
permission-modedontAsk见上一节
toolsRead,Glob,Grep,Bash启用的工具,逗号分隔
allowed-tools空预批规则,逗号分隔
base-url空模型网关地址;空则读调用方仓库变量 WAVE_BASE_URL
model空模型名;空则读调用方仓库变量 WAVE_MODEL(必需)
timeout-minutes20整个 job 的超时;审查类提示词不建议小于 20
输出说明
result最后一条助手消息的纯文本
subtypesuccess / error_during_execution
is-errortrue 表示这次运行失败
result-file结果 JSON 在工作区里的路径

model / base-url 留空时会读调用方仓库的 Variables,所以绝大多数仓库只需要配一次 WAVE_MODEL 变量 + 一个 WAVE_API_KEY secret,调用方 with: 里只写 prompt。想写死也可以显式传 with: { model: deepseek-flash }。

用输出串联下游 job:

yaml
jobs:
  review:
    uses: netease-lcap/wave-agent/.github/workflows/wave-review.yml@main
    secrets: inherit
    with:
      prompt: /code-review
  report:
    needs: review
    if: needs.review.outputs.is-error == 'false'
    runs-on: ubuntu-latest
    steps:
      - run: echo '${{ needs.review.outputs.result }}'

9.5 GitHub Actions:自己写一个 step ​

不想依赖本仓库的 workflow,就让 job 自己装 CLI:

yaml
name: Review

on:
  pull_request:

jobs:
  review:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0 # 审查要算 merge-base,浅克隆会退化成 HEAD~1
      - uses: actions/setup-node@v5
        with:
          node-version: 22
      # 生产环境把 latest 换成具体版本:上游发版即改变流水线行为
      - run: npm i -g wave-code@latest
      - name: Review
        env:
          WAVE_API_KEY: ${{ secrets.WAVE_API_KEY }}
          WAVE_MODEL: ${{ vars.WAVE_MODEL }} # 如 deepseek-flash;CI 里没有本地配置可继承
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          wave -p '/code-review' \
            --output-format json \
            --permission-mode dontAsk \
            --tools 'Read,Glob,Grep,Bash' \
            > wave-result.json
      - if: always()
        uses: actions/upload-artifact@v4
        with:
          name: wave-result
          path: wave-result.json

两处容易踩:

  • fetch-depth: 0 不能省。内置审查技能用 git merge-base HEAD main 定位改动范围,浅克隆下算出的范围不是这个 PR。

  • 本地没有 main 时会退化。技能里那行是 git merge-base HEAD main 2>/dev/null || echo HEAD~1;pull_request 事件检出的是 PR 的 merge commit,HEAD~1 恰好落在 base 分支尖上、等于没评。要让审查按 base 分支算,先建本地分支:

    yaml
    - run: git fetch --no-tags origin "refs/heads/${GITHUB_BASE_REF}:refs/heads/${GITHUB_BASE_REF}"

--allowed-tools 在这里没给:/code-review 的 allowed-tools 已经包含 git diff* / git log* / git show* / git merge-base* / gh pr view*,执行技能时会自动注册成临时规则。换成自己的提示词时,才需要按 9.3 的表格补。

9.6 GitHub Actions:变成阻断合并的门禁 ​

  1. 跑通一次上面的 job;
  2. 仓库 Settings → Branches → 分支保护规则 → Require status checks to pass,勾上这个 job 的名字;
  3. 之后这个 check 变红时 PR 不可合并。

红绿来自 CLI 的退出码,判据见 9.8。

9.7 GitLab CI ​

把下面这段合进 .gitlab-ci.yml(或 include 指向 ci/gitlab-ci.yml 的 raw URL),并在 Settings → CI/CD → Variables 里加一个 masked 变量 WAVE_API_KEY:

yaml
wave-review:
  image: node:22
  variables:
    WAVE_VERSION: "latest" # 生产请 pin 具体版本
    WAVE_PROMPT: "/code-review"
    WAVE_MODEL: "deepseek-flash"
    WAVE_TOOLS: "Read,Glob,Grep,Bash"
    GIT_DEPTH: "0"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  before_script:
    - npm i -g "wave-code@${WAVE_VERSION}"
  script:
    - |
      set -uo pipefail
      status=0
      wave -p "$WAVE_PROMPT" \
        --output-format json \
        --permission-mode dontAsk \
        --tools "$WAVE_TOOLS" > wave-result.json || status=$?
      if [ -s wave-result.json ]; then
        node -e 'const r=require("./wave-result.json");console.log(r.subtype, r.result ?? "")'
      fi
      exit "$status"
  artifacts:
    when: always
    expire_in: 1 week
    paths:
      - wave-result.json

GIT_DEPTH: "0" 与 GitHub 侧的 fetch-depth: 0 同因;node:22 镜像里没有 jq,读结果用镜像自带的 node -e。

9.8 结果与产物 ​

结果文件就是 --output-format json 的那一条信封,字段表见 结构化输出。CI 里最常用的是四个:

字段用途
subtypesuccess / error_during_execution
is_error布尔,等同退出码的语义
result结论正文;失败版没有这个字段
errors失败原因,仅在非空时出现

退出码只看两件事,满足任一即为 1:建 agent 或发消息抛错;运行结束时错误块非空且一句正文都没产出。工具执行失败不算运行失败(记在 tool_calls[].success 上)。

9.9 局限 ​

  • fork 来的 PR 拿不到 secret:GitHub 不把 WAVE_API_KEY 交给 fork PR 的 workflow,这类 PR 上 job 会失败。要么改用 pull_request_target 并只做只读操作,要么由维护者在评论里手动触发。
  • 版本要 pin:wave-version 默认 latest,上游发版即改变流水线行为。
  • 一次运行没有记忆:每个 job 都是新会话,上一轮审查的结论不会带到下一轮。