Nova-Code 使用手册

Nova(命令行二进制 nova)是一个跑在终端里的编码 agent —— 它读代码、跑命令、改文件,通过工具调用把一项任务推到完成。模型层面向国产大模型,内置两套传输协议:Anthropic 兼容(DeepSeek 与 Kimi/Moonshot 原生)与 OpenAI 兼容(Qwen / GLM / MiniMax / 豆包等 chat/completions 端点原生)。DeepSeek(默认 deepseek-v4-pro)与 Kimi(Moonshot)有专用 provider profile,其余 Anthropic 兼容端点走通用档 other(OpenAI 兼容端点不设独立 provider —— 在供应商自己的 profile 上把 settings.transport 设为 "openai" 即可)。

本手册面向使用者:怎么装、怎么配、怎么在日常里把它用顺。若想了解内部架构(loop 契约、包依赖、扩展点),请读仓库根的 CLAUDE.mdREADME.md

21 章 · 单文件文档 国产模型深度定制 MIT
01

它能做什么

  • Agentic 编码循环 —— 给它一个目标,它自己读代码、改文件、跑命令,一步步把任务做完。同一轮里互相独立的工具调用以有界并发运行(默认每轮 3 个)。max_tokens 截断可自动续传(默认最多连续 3 次,可配),单次响应写不完不再是硬墙。
  • 代码与系统工具 —— 文件 read / write / edit(文本、表格、PDF、图片都能读),glob + grep 搜索,bash(默认与上限均 3 分钟超时,run_in_background 转后台长任务),web fetch / search,向你提问。
  • LSP 代码智能 —— 直连语言服务器做 go-to-definition、find-references、hover、诊断、符号搜索,比 grep 更懂作用域和类型。
  • 扩展 thinking —— 五档(off/low/medium/high/max)或显式 token 预算。
  • Plan 模式与子 agent —— 把庞大的调查/规划交给全新上下文的 worker,结论汇报回来,主对话保持干净。内置三种类型 + 支持自定义。
  • 记忆、Skills、自定义命令、MCP、Hook —— 项目 / 用户级的知识与工具扩展;Shell 钩子在工具调用 / 用户提交 / 压缩等生命周期节点插入自定义逻辑。
  • 多语言 —— 模型回复语言(language)与 TUI 界面语言(locale,内置 zh-CN / EN)分开配置,默认都跟随系统 locale。
  • 权限与沙箱 —— 工作区信任门 + 基于规则的拦截 + OS 级文件写入隔离,三层防护。危险 bash 模式(rm -rf /、fork bomb、mkfs 等)直接拒绝,不可绕过。
  • 可恢复会话 —— append-only 持久化,随时 --resume / --continue/rewind 回到更早节点(含文件改动预览确认)。
  • 国产模型深度定制 —— thinking 按各家 wire 形状映射(DeepSeek 的 output_config.effort、Kimi 的 thinking.type),请求结构对自动上下文缓存友好,错误码翻译与瞬时重试。模型按 lite/pro/max 三档配置(DeepSeek 内置默认:litedeepseek-v4-flash-vision-exppro/maxdeepseek-v4-pro,各档 thinking 深度不同),默认档位 pro,支持 /model 运行时切换。
02

安装与环境要求

环境要求:

  • Node ≥ 20(仓库 .nvmrc 已固定)
  • pnpm 10.28.2packageManager 已固定)
  • 交互模式需要真正的终端(TTY)渲染全屏 REPL;没有 TTY(管道 / 重定向 / CI / git hook)时不会报错,而是自动进入 headless 模式跑一轮后退出(见 §4)。

从源码运行:

bash
pnpm install
pnpm dev                       # 启动 REPL(tsx 运行 apps/cli/src/index.ts)
pnpm dev "帮我给这个函数加单测"   # 先跑一轮 prompt,再进入 REPL
echo "总结这个 diff" | pnpm dev  # 无 TTY:headless 跑一轮后退出
开发态与发布态

pnpm dev 是开发态入口;发布后的二进制名为 nova,本手册中 nova …pnpm dev … 等价。

03

首次配置向导

第一次启动时,如果 ~/.nova/nova.config.json 里缺少 apiKey,Nova 会进入首次配置向导。它从内置 provider 模板里取一个(模板已填好 baseURL、默认档位,lite/pro/max 模型表则由 provider 的内置默认表提供),所以唯一要交互问你的就是 API key(输入时掩码)。

当前只有 DeepSeek 一个模板对外可选(Moonshot/Kimi 已内置但在内部测试期,暂从选择器隐藏;「Other provider」手填入口也暂时关闭)。既然只有一个 provider,向导会跳过选择器,直接问 DeepSeek 的 API key。它写入:

  • provider: deepseektransport: openaibaseURL: https://api.deepseek.com(DeepSeek 的 OpenAI 兼容端点)、默认档位 pro(以及 goal 配置和 key 本身)
  • 模型表不写盘litedeepseek-v4-flash-vision-exppro/maxdeepseek-v4-pro(三档靠 per-tier thinking 拉开梯度)来自代码里的内置默认
默认模型表不落盘

models 的默认值按 provider 内置在代码里,加载配置时才层叠进来;配置文件里只放你自己的覆盖项。这样 Nova 升级带来的新模型 id、新价格、新上下文窗口,老装机也能直接吃到,而不会被向导当年写进文件的那份表钉死。旧版本写过整张表的配置,启动时会被改写成它实际表达的覆盖项(通常是空的,或一条 /effort 设过的 thinking),取值完全不变。

想覆盖某一档,只写要改的字段即可,例如 "models": { "pro": { "thinking": "low" } } —— 其余字段(idpricingmaxTokens…)继续跟随内置默认。但如果你把某档的 id 改成别的模型,该档就整条以你的为准(不再继承内置的价格与上限);反过来,同 id 的档位无法「删掉」某个内置字段(省略即继承)。/effort 持久化写的也正是这种最小覆盖。

Ctrl+C 可中止向导。apiKey 已存在则跳过向导;导出了环境变量 NOVA_API_KEY 且配置里已有 models 表时同样跳过(只有环境变量、还没有 models 表时,向导仍会跑,但不再问你 key,也不会把这个 key 写进配置文件)。要接别的端点,直接手动编辑 ~/.nova/nova.config.json —— provider 与传输协议(transport)是两个独立维度:同一家供应商(如 DeepSeek)可同时有 Anthropic 兼容端点(https://api.deepseek.com/anthropic)与 OpenAI 兼容端点(https://api.deepseek.com),换协议只需改 transport + baseURL,DeepSeek 的错误翻译 / 余额探针 / 文档链接原样保留。通用档 other(通用 Anthropic 兼容端点)无内置档位表,需按 lite/pro/max 三档骨架填全,完整字段见 §20。OpenAI 兼容端点不设独立 provider:在现有供应商 profile 上把 transport 设为 "openai" 即可。

apiKey 仍为空?

如果启动时 apiKey 仍为空(且没有 NOVA_API_KEY),Nova 会报错退出并提示去配置文件里补上。

04

启动与命令行参数

text
nova [prompt...]                   # 先跑一轮初始 prompt,再留在 REPL
  -p, --prompt <text>              # 无头模式:跑单轮后打印结果退出(不进入 REPL)
  -m, --model <tier>               # 临时切换模型档位(只认已配置档位名,如 lite/pro/max)
  -t, --think off|low|medium|high|max   # thinking 等级,或正整数 token 预算
      --max-turns <n>              # 单轮最大循环次数
      --cwd <dir>                   # 工具的工作目录(工作区根)
      --resume <id>                 # 恢复指定 id 的 session
  -c, --continue                  # 恢复最近一个 session
      --no-transcript             # 本次不写 transcript
      --no-pretty                 # 关闭 pretty 日志
      --output-format text|json|jsonl  # headless 输出格式(默认 text)
      --permission-mode default|acceptEdits|auto|plan  # 初始权限模式(默认 auto)
      --dangerously-skip-permissions    # 自动批准所有权限提示(无头模式等效 allow)
  -v, --version                   # 打印版本后退出

子命令(各自独立,不进 REPL):

bash
nova doctor                        # 体检全局配置并打印报告(同 REPL 内 /doctor)
nova mcp …                         # 管理 MCP 服务器(连接测试、认证等,见 §16)
nova plugin …                      # 安装 / 启停 / 列出插件(见 §18)
nova upgrade                       # 跑配置里的安装器升到最新版(见 §19)

要点:

  • 位置参数即初始 promptnova 把 README 翻译成英文 会先跑这一轮,再停在 REPL 等你继续。
  • -p / --prompt 是无头模式:传了这个 flag,Nova 跑一轮后直接打印结果并退出,不进入交互式 REPL。如果 stdin/stdout 不是 TTY(管道、重定向、CI),也会自动进入无头模式。用 --output-format json 可以拿到结构化 JSON。
  • -m / -t / --max-turns 都是本次会话的临时覆盖,不写回配置文件。
  • --cwd 决定工具的「工作区根」—— 读写权限、沙箱写入范围、工作区信任判定都以它为基准(见 §10)。
  • 首次在某个目录启动会先问「是否信任这个文件夹」;headless 弹不出确认框,未信任的工作区直接失败退出(见 §10)。
  • --permission-mode 启动时直接设定权限模式(REPL 内也可用 Shift+Tab 切换)。--dangerously-skip-permissions 对所有权限提示自动批准,适合无人值守场景。
  • session 列表 在 REPL 内用 /resume(不带参数)的交互式选择器查看,无独立 CLI 参数。
05

交互式界面(TUI)

Nova 是一个全屏 Ink/React REPL:顶部是滚动的历史区,底部是固定的输入框和实时状态行,支持流式输出、鼠标滚动与选区。

全局按键

按键作用
Enter提交当前输入
Esc中断正在运行的回合
Ctrl+C有回合在跑时中断它;空闲时再按一次退出
Ctrl+D退出 Nova
Shift+Tab循环切换权限模式:default → accept edits → auto → plan(见 §10
Ctrl+V从剪贴板粘贴:图片(截图 / 复制的图)落盘后以路径形式插入并登记为附件,否则按普通文本粘贴
鼠标滚轮在历史区上下滚动
鼠标拖拽选中文本(用于复制)

把图片文件拖拽到终端窗口上同样有效 —— 路径会被规范化成绝对路径并当作附件插入。图片能否真的送进模型,取决于当前档位是否支持图像输入(见 §9 read)。

输入框编辑键(emacs 风格)

按键作用
Ctrl+A / Ctrl+E跳到行首 / 行尾
Ctrl+U / Ctrl+K删除到行首 / 删除到行尾
Ctrl+W往前删一个词
/ 浏览输入历史 / 在补全弹窗里移动

选择类弹窗(审批、提问、session 选择器)

  • 上下移动:/,或 j/k(审批框),或 Ctrl+P/Ctrl+N(选择器)
  • 确认:Enter;取消/拒绝:Esc
  • 多选题用 空格 勾选
  • 审批框还支持 PageUp/PageDown 滚动周围视口,方便先看清要批准的内容

@path 文件引用补全

在输入框里打 @ 开头的词,会弹出工作区文件路径补全。文件清单与 glob/grep 看到的一致:遵守 .gitignore(逐层向仓库根收集)、跳过 node_modules/.git、不含隐藏文件,最多 10000 条。

排序由具体到宽泛:文件名前缀命中 > 文件名内含 > 整条路径内含,同分则短路径优先。/ 选择、EnterTab 补全 —— 补全只替换那个 @… 词,不会提交。命令行(/ 开头)上只弹 slash 命令补全,不触发 @

! shell 直通

! 开头的一行不作为 prompt 发给模型,而是直接在 shell 里跑(输入框边框会变绿提示)。它复用 bash 工具:同样在工作区根执行、同样受 OS 沙箱约束(见 §11),输出以一张卡片贴回历史区,Esc 可中断。因为是你自己敲的命令,不走权限门

命令跑完后,这次执行会以 <bash-input> / <bash-stdout> / <bash-stderr> 三段记录进入上下文,并立刻触发一轮模型回应 —— 所以 ! git diff 之后可以直接接「解释一下上面的改动」,模型已经看过了。这条记录标为 synthetic,历史区不会因此多出一个用户气泡(! 卡片已经展示了这次执行);Esc 中断的命令和空的 ! 都不入上下文、也不触发回合。

它和 / 命令行一样带本地副作用,所以即使在回合运行中键入也始终排队、由 REPL 在空闲时派发 —— 不会被 queue.consumeInLoop 折进当前回合(见 §20)。

状态行

输入框下方两行实时状态:

  • 第一行:模型档位 + 思考等级 + 上下文窗口、上下文占用进度条与百分比、工作区目录名、git 分支、完整 cwd。
  • 第二行(用量):账户余额(仅接 DeepSeek 官方 API 时出现;账户不可扣费时转为琥珀色)、缓存命中率、累计输入 / 输出 token。花费估算不再上状态行(价格变动频繁,过期的估算比不显示更糟),改由 /usage 查看。终端过窄时从右往左丢弃分段,余额和命中率优先保留。
    • 缓存分段给两个数字:缓存 90% 会话 99% 累计 —— 前者是本会话(从 transcript 重放,扛得住重启和 /resume)的命中率,后者是跨会话累计的命中率,/clear、换会话、重装都不归零。累计值记在 ~/.nova/usage.json(每请求批量累加、原子写,多个 nova 进程会各自并入而不是互相覆盖),而不是启动时去扫 ~/.nova/sessions/* —— 旧会话目录会被 sessionCleanup.maxAgeDays 清掉,扫出来的历史本来就不全。哪一半还没有数据就只显示另一半。

界面语言(i18n)

  • settings.language 决定模型回复语言(注入 system prompt),默认 auto —— 跟随系统 locale($LC_ALL/$LANG/$LANGUAGE,macOS 还会读 AppleLocale)。
  • settings.locale 只覆盖 TUI 静态文案(菜单、提示、状态行),默认 auto 即跟随 language。内置 zh-CN 与 EN 两套文案,不认识的标签一律回落到英文。
  • 两者可以不同 —— 比如中文界面 + 英文回复:{"locale": "zh-CN", "language": "en"}。改动需重启生效(语言写进 system prompt,会话中途变更会击穿前缀缓存)。

输入预测

每成功跑完一轮,Nova 会用主模型预测你下一句可能想说什么,作为输入框的灰色占位提示(默认开启,超时 8s,最多 300 字)。用 /predict on|off 开关,或在配置里调 predict

06

Slash 命令大全

在输入框里以 / 开头即触发。内置命令永远优先于自定义命令

命令作用
/help显示帮助;列出按来源分组(Built-in / Project / User)的命令
/effort [<level>]查看或切换 thinking 等级(off/low/medium/high/max 或整数预算)
/model [<tier>]查看或切换当前会话的模型档位lite/pro/max 等已配置档位,仅本次会话不持久化);只接受配置过的档位名,裸模型 id 会被拒绝;无参数弹出交互列表
/clear清空当前会话历史(session 仍保留)
/rename [<name>|clear]给当前 session 起个名字(显示在输入框边框上);clear 清除
/compact [focus…]把历史压缩成单条摘要消息;可附带关注点提示
/resume [<id>]切换到指定 session;不带参数则弹出列表选择
/rewind [<n>]回退到此前某条消息 —— 其后的对话历史与文件改动都会被丢弃(会预览受影响的文件并请求确认)
/init [focus…]探索代码库后生成 / 刷新项目记忆(NOVA.md,可附关注点)
/plan <goal>把调查交给一个只读的 plan 子 agent,返回分步实现计划
/goal [<condition>|clear]设定一个成功条件,Nova 自动推进直到达成;clear 取消
/diff [pathspec]交互式浏览未提交变更:列出文件列表(含 staged/unstaged 状态),选中后查看语法高亮差异(只读)
/review [focus…]审查当前未提交的 diff,只读地报告问题(不改动任何文件)
/review <PR#|#PR|PR-URL> [focus…]通过 gh CLI 只读审查某个 GitHub PR(gh pr view / gh pr diff);gh 缺失或未登录会明说并停下
/usage显示当前会话累计 token 用量和缓存命中率;若配置了价格则估算费用
/context可视化上下文窗口占用:按类别(system prompt、memory、skills、tools、MCP、messages + 空闲空间)分解为彩色条形图
/predict [on|off]查看或切换「下一条输入预测」
/commands [reload]列出已注册的 slash 命令;reload 重新扫盘加载自定义命令
/skills列出已发现的 SKILL.md(及各自来源)
/agents [reload]列出可用的子 agent 类型;reload 重新扫盘加载 agent 文件
/agent <name> <task>把一项任务委派给指定的子 agent
/nova-code-guide <question>就 Nova 自身答疑的只读 Q&A 子 agent;/nova-code-guide-update 拉取 / 刷新它读取的 Nova 源码
/loop <interval> <prompt|/cmd>按固定间隔重复投递某条 prompt 或命令;/loop stop 停止,/clear//resume/退出 亦终止
/doctor体检全局配置(JSON/schema、模型/key、项目 hook 文件、MCP 摘要)并在弹窗里报告;按 f 把问题交给 agent 就地修复
/mcp [tools]打开 MCP 服务器菜单(认证 / 重连 / 登出,见 §16);tools 列出所有桥接的工具
/lsp查看已配置的语言服务器(是否在 PATH、本 session 是否已启动)
/plugin列出已加载的插件及其贡献(安装 / 启停用 nova plugin CLI,见 §18
/sandbox [on|off]本会话内开关 OS 命令沙箱(见 §11
/tasks [list|stop <id|all>]查看和管理后台命令(bash + run_in_background),支持 list / stop
/exit, /quit退出

/diff 是交互式只读差异查看器;/review 展开成引导 prompt 交给主 agent,让它用自己的工具去查 git status/diff 并完成审查。/context/usage 分别展示上下文占用的即时快照和累计用量。/model 只按已配置的档位名切换(如 /model pro),裸模型 id 会被拒绝。token 流式渲染由配置项 stream.enabled 控制(无对应 slash 命令)。/help/commands 会把内置 + 项目 + 用户三层命令都列出来;自定义命令的加载规则见 §15

07

思考等级(Thinking)

Nova 把「extended thinking」暴露成五个等级,或一个显式的 token 预算。在 DeepSeek 上,等级映射到 output_config.effort(而不是 Anthropic 的 budget_tokens)。

off low medium high max
  • 五档:off / low / medium / high / max
  • 显式预算:传一个正整数(如 -t 4096),它会覆盖等级映射,直接当作 budget_tokens

思考等级是 per-tier(按档位)的属性,没有全局 thinking 配置项 —— 它写在 models.<tier>.thinking 里,切档(/model)会把当前思考等级换成该档的值。这也是 lite/pro/max 能在同一个模型 id 上拉出能力梯度的原因(DeepSeek 内置默认:lite→low、pro→high、max→max)。档位没写 thinking 时回退到 max

设置方式:

  • 启动时:nova -t high "..."nova -t 4096 "..."(本次会话临时覆盖)
  • 运行时:/effort high(查看用 /effort)—— 写回当前档位,本会话内生效
  • 持久:改配置里该档的 models.<tier>.thinking

更深的思考通常带来更好的规划,但更慢、更贵 —— 按任务难度调档即可。

08

Plan 模式与子 Agent

子 Agent

模型可以用 createSubAgent 工具把活儿派出去。子 agent 在进程内运行,带全新上下文(永远看不到父对话),工具集是父 agent 的工具减去 createSubAgent 本身 —— 所以不会递归。内置类型:

类型工具权限用途
explore只读(无 write/edit/bash)检索定位代码,汇报路径 / 调用点
plan只读(无 write/edit/bash)调查后给出分步实现计划
general-purpose完整工具需要真正改文件或跑命令的活儿
nova-code-guide只读(限于 Nova 源码检出)就 Nova 自身答疑(/nova-code-guide

同一轮里的多个 createSubAgent 调用会并发执行(受 toolConcurrency 限制)。父 agent 只会收到每个子 agent 的最终一条消息 —— 庞大的中间调查被挡在主上下文之外。

通过 settings.subagent 配置:enabled / model / maxTurns(默认 5000)/ maxTokens(默认 32768,独立于顶层的每响应输出上限)。model 是一张按子 agent 名索引的表(如 {"plan":"max","explore":"pro"}),可给每个 agent 单独指定模型档位;解析顺序由具体到宽泛:该表的对应条目 → 内置默认(general-purpose/planmaxexplore/nova-code-guidepro)→ 自定义 agent 自己 frontmatter 里的 model → 当前主模型;整张表省略则全部沿用默认。每个子 agent 的 transcript 落在 ~/.nova/sessions/{id}/subagents/

子 agent 调用 todo / task / 长任务这类「有状态」工具时,操作的是父 session 的共享存储。

自定义子 Agent 类型

除三种内置类型外,你还能用 .md 文件定义自己的子 agent 类型。文件包含 YAML frontmatter 和正文(system prompt):

  • 项目层.nova/agents/(兼容 .claude/agents/
  • 用户层~/.nova/agents/(兼容 ~/.claude/agents/

Frontmatter 字段:

字段必填说明
name唯一类型名(小写字母数字+连字符,如 code-reviewer
description给父 agent 看的描述(≤200 字符)
tools可选工具白名单(如 [read, grep, glob]),与父工具集取交集
readOnly可选(默认 false)若 true,剥夺 write/edit/bash
model可选覆盖子 agent 的模型
maxTurns可选覆盖子 agent 的最大轮数(默认 5000;语义同顶层:单条消息内模型调用轮次上限,触顶给「禁用工具、立即收尾」轮,不丢已收集信息)
maxTokens可选覆盖子 agent 的单次输出上限
  • /agents 列出当前可用的全部子 agent 类型及来源;/agents reload 改完文件后重新扫盘。
  • /agent <name> <task> 把一项任务直接委派给指定的子 agent 类型。
  • 发现目录可用 settings.subagentprojectDirs / userPaths / extraDirs 覆盖。
  • 优先级:内置类型永远赢 → 项目层覆盖用户层 → 同层先到先得。

/plan 命令

/plan <goal> 是上面机制的一层薄封装:它让 agent 派生一个 plan 子 agent,对目标做只读调查,然后返回一份分步实现计划 —— 动手改动之前先看清楚要做什么。

09

内置工具一览

下面是模型可调用的全部内置工具。标 只读 的默认自动放行;标 需批准 的默认走权限引擎询问(见 §10)。

文件与文本

工具权限说明
read只读文本 / 表格 / PDF / 图片:文本输出带 cat -n 风格行号(1-based),offset 起始行、limit 最大行数,单页约 20 万字符上限、单行超 1.6 万字符会截断并标注,超出时提示用 offset 续读;行号前缀仅用于显示,传给 edit 前需去掉。表格(.xlsx/.xls/.xlsm/.xlsb/.ods)每行渲成 TSV 带表头,sheet 选工作表。PDF.pdf,≤30MB)抽取文本后同样带行号返回,每页前插一条 [Page N] 标记,offset/limit 照常分页;扫描件 / 纯图片 PDF 抽不出文本,会明说并建议改用 OCR 工具。图片(.png/.jpg/.jpeg/.gif/.webp)返回文本元数据,并将 base64 图片作为紧随工具结果的用户图片消息交给模型;最长边超过 2048px 时先在内存中等比缩小,不改写原文件 —— 仅当前档位支持图片输入时(否则提示切到 image-capable 档位)。含 NUL 字节的二进制文件直接拒读并给出 file/xxd 建议
write需批准写整个文件(覆盖),默认自动创建父目录
edit需批准精确字符串替换;old_string 默认须唯一匹配,replace_all 可全替
bash需批准执行 shell 命令(bash -lc)。默认阻塞:timeout_ms 默认与上限都是 180000(3 分钟)cwd 可覆盖工作目录,输出截到 200KB。更长的任务传 run_in_background: true——命令转入后台、立即返回 {id, pid, output_path}env 可追加环境变量

搜索与发现

工具权限说明
glob只读按 glob 模式列文件;默认遵守 .gitignore,永远跳过 node_modules/.git
grep只读ripgrep 搜内容;支持大小写 / 字面量 / 上下文行 / 仅列文件名等,硬超时 30s

Web

工具权限说明
webfetch只读取单个 http(s) URL,转成 markdown/text/html;遵守 robots.txt,默认超时 30s
websearch需 API key搜公网返回 title+url+snippet;需配置 websearch.braveApiKey / tavilyApiKey / serperApiKey,或对应环境变量 BRAVE_SEARCH_API_KEY / TAVILY_API_KEY / SERPER_API_KEY(按序自动选)

代码智能

工具权限说明
lsp只读一个工具六个 action:definition/references/hover/diagnostics/document_symbols/workspace_symbol。坐标对模型是 1-based。详见 §17

与用户交互

工具权限说明
askUserQuestion只读一次提 1–4 个选择题(每题 2–4 个选项,可多选);运行时自动补一个「Other」让你自由作答
enterPlanMode默认放行模型把当前会话切进只读的 plan 权限模式(与 Shift+Tab 切到的那一档是同一个开关)。只会收权,所以不问你;界面上显示为一行 plan read-only,标出会话是从哪里开始只读的
exitPlanMode默认放行带上完整方案(markdown)请你拍板,直接弹一个「按这个计划动手吗?」的选择框。它的调用行不显示,取而代之的是 plan 参数当作模型正文渲染出来 —— 那正是要你拍板的东西(模型若已在正文里写过同一份方案,则不重复渲染)。只有你明确同意才关掉 plan 模式(回到进入前的那个模式,没记录则回 default);选「其他」写的意见会作为反馈回给模型继续改,选「不同意」而不写意见、或直接 ESC 关掉,则当场结束这一轮回到输入框 —— 几种情况 plan 模式都保持开着

模型自己进出 plan 模式这套由 planMode.agentTools 控制(默认开)。关掉后这两个工具不再注册,plan 模式就只能靠 Shift+Tab--permission-mode plan 手动进。子 agent 永远拿不到这两个工具 —— 它跑在主会话里,不该去改主会话的权限模式。

不过退出 plan 模式并不依赖模型调 exitPlanMode:只要一轮结束时还在 plan 模式,nova 自己就会弹那个确认框(planMode.approvalGate,默认开),见 §10

计划管理:Todo(会话内,内存态)

createTodo / updateTodo / getTodoList / clearTodoList —— 把多步计划外化成一张清单,只存内存,进程退出即丢。任一时刻最多一个 todo 处于 in_progress。全部完成后清单会在 todo.autoClearDelayMs(默认 2.5s,留一拍让你看到那排 ✓)后自动清空,不指望模型自己去调 clearTodoList

计划管理:Task(工作区内,落盘持久)

createTask / updateTask / getTaskList / clearTaskList —— 更大、值得跨会话保留的计划,落盘到工作区的 .tasks/{id}.json。支持 blockedBy 依赖关系,允许多个并行 in_progress。整份计划做完后同样会在 task.autoClearDelayMs(默认 2.5s)后自动清空并删掉对应的 .tasks/ 文件。

定时调度:Cron(会话内,落盘持久)

工具权限说明
cronCreate只读放行把一条 prompt 或 /command 排进定时表:schedule 支持重复间隔30s/5m/1h)或标准 5 字段 cron 表达式0 9 * * **/15 * * * *);可选 labelmaxIterations
cronList / cronDelete只读放行列出 / 删除定时表条目

定时条目落盘在 ~/.nova/sessions/{id}/cron//resume 时重新装载并重排,/clear 时清空。只有会话活着时才会触发(没有后台守护进程)—— 到点的 tick 若正好有回合在跑,会等 REPL 空闲后立刻补跑,不重叠堆积。三个工具本身默认放行(只登记元数据),但排定的 payload 真正动手时仍在触发那一刻走完整权限门/loop 就是基于这套机制的薄封装(见 §6)。配置见 §20 cron.*

后台长任务

工具权限说明
bashrun_in_background: true需批准后台起一个命令(dev server / watcher 等),立即返回 {id, pid, output_path};session 退出时子进程被杀。可传 env 追加环境变量
killBackground需批准按 id 终止后台命令(先 SIGTERM,残留则 SIGKILL);已退出则无操作
monitor需批准起一个监听脚本:它 stdout 的每一行都变成一条通知推给模型。用于 tail -finotifywait -m、轮询循环。返回 {id, pid, watching, persistent, log_path}
stopMonitor只读按 id 停掉一个监听

后台命令没有专门的读取工具:output_path 指向 ~/.nova/sessions/{id}/background/{命令id}.log,这是命令 stdout+stderr 的完整日志,用普通的 read / grep 跟读即可(该目录已默认放行,不会每次弹权限)。

monitor 与后台命令的分工,按「你要被通知几次」划分:只要一次(构建结束、服务起来了)用 bash + run_in_background 配一个条件满足就退出的命令;每次发生都要才用 monitor。过滤写在命令里(grep -E --line-buffered)且必须覆盖失败——只匹配成功标记的过滤器在崩溃时同样沉默,而沉默和「还在跑」无法区分。stderr 不是事件流,只进 log_path。超过 settings.monitor.maxEventsPerWindow(默认 60 条/分钟)的监听会被直接杀掉并在通知里说明。

命令结束时会由 <background-notification> 自动注入一条公告——只带 id、状态、退出原因和日志路径,不内联输出。这样输出只有一条投递通道(文件):若通知里也内联一份,模型自己 read 过的内容就会被重复推送一遍,而且一个跑久的 dev server 结束时可能一次性往 append-only 历史里灌进上百 KB。状态和退出原因留在通知里,已经足够模型判断要不要再花一轮去读日志。

Skills 与子 Agent

工具权限说明
loadSkill只读按名加载某个 SKILL.md 的完整正文(响应上限 16KB,可配)
createSubAgent派生本身放行§8;子 agent 内部的工具调用会被重新走一遍权限

MCP 桥接进来的工具以 mcp__<服务器>__<工具> 命名,同样受权限引擎管控(见 §16)。

10

权限与安全

工作区信任(启动前的第一道门)

在任何工具能跑起来之前,Nova 先确认你允许它访问当前这个文件夹。首次在某个目录启动时会占满屏幕弹一张确认卡片,选 Yes 才继续,选 No / Esc 直接退出。

  • 信任记录写在用户全局配置 ~/.nova/nova.config.jsontrust.trustedRoots(绝对、符号链接已解析的路径),从不写进项目内的文件 —— 所以 clone 下来的仓库无法把自己标记为可信。
  • 判定是包含关系:工作区等于或位于某个已记录根之下即视为可信,因此信任仓库根即覆盖其所有子目录。
  • 家目录例外:对 ~ 授予的信任只在本次会话有效,不落盘。
  • headless(-p / 无 TTY)没法弹确认框,所以未信任的工作区是硬性拒绝:先交互式跑一次并信任、或手工把路径加进 trust.trustedRoots、或传 --dangerously-skip-permissions 绕过。
  • 写配置失败不会中断会话(你已经同意了,本次有效,只是下次还会再问)。设 trust.enabled: false 可恢复「启动即信任」的旧行为。

权限引擎

权限引擎(@nova/safetyPermissionEngine)是模型与文件系统之间的一道闸门。每次工具调用按下面的顺序裁决:

  1. 1危险 bash 直接拒绝。命令若匹配内置危险模式(rm -r /、fork bomb、mkfsdd if=… of=/dev/…、重定向到块设备),立即 deny,无法绕过。
  2. 2运行时放行表。你在审批时选过「Always allow this tool」的,记在内存里,本 session 内同工具直接放行。
  3. 3配置规则(首个匹配生效)。按顺序遍历 permissions.rules,第一条命中的决定结果。
  4. 4默认效果兜底。都没命中就用 permissions.defaultEffect(默认 ask)。
  5. 5出错降级为 ask。规则求值若抛异常(坏正则等),降级为询问而非崩溃或拒绝 —— 让你始终掌握控制权。

权限模式(Shift+Tab 切换)

输入框右下角有一个权限模式指示,按 Shift+Tab 在四档间循环(bypassPermissions 仅在 --dangerously-skip-permissions 启用后才加入循环)。不指定 --permission-mode 时,启动即为 auto。它在权限引擎之前介入,临时改变写类工具的裁决倾向 —— 只影响当前会话、不写盘:

模式状态行行为
default○ manual mode on不改变任何裁决,write/edit/bash 照常落到引擎的 ask
acceptEdits⏵⏵ accept edits on工作区内write/edit 自动放行(不再逐次询问);bash 与工作区外的写仍然询问
auto
启动默认
✦ auto mode on自主模式:在 acceptEdits 基础上,命令工具(bash,含 run_in_background)也自动放行、无人值守运行(先过一层风险分类器)。比 acceptEdits 更宽,但仍窄于 bypassPermissions —— 工作区外的写和用户 deny 规则不被绕过
plan⏸ plan mode on只读write/edit/bash 一律拒绝,逼模型先调查、给出分步计划,而不动手改 —— 与只读 /plan 子 agent 同源

权限模式只是「在引擎之前」加一层偏置:acceptEdits/auto 放宽写类(以及 auto 的命令)工具、plan 只收紧写类工具,其余裁决(危险 bash 拒绝、配置规则、读工具放行等)一律不变。想退出 plan 模式动手改动,再按 Shift+Tab 切回即可。

plan 这一档模型自己也能进:enterPlanMode 让它在动手前先切成只读(只收权,不问你)。整套由 planMode.agentTools 控制,默认开;见 §9。模式无论谁切的,都会在下一次请求时以一条 <plan-mode> 提示告诉模型,行为完全一致。

退出则不靠模型自觉。 只要一轮结束时会话还停在 plan 模式,nova 在回到输入框之前会自己弹确认框:

  • 同意 → 权限模式立刻回到进入 plan 之前的那一档(shift+tab 进的也记得,例如从 auto 进就回 auto),并立刻续跑一轮开始实现,你不用再输入任何东西
  • 不同意 → 留在 plan 模式,本轮到此结束,回到输入框等你下一条消息(不写意见就等于「先停一下」,模型不会自己再改一版方案)
  • 在「其他」里写意见 → 同样留在 plan 模式,但你写的内容直接作为下一轮输入回给模型,让它照着改
  • ESC 关掉 → 什么也不做,回到输入框(shift+tab 和直接打字都照常)

模型主动调 exitPlanMode 弹的是同一个框、同一套恢复逻辑;那一轮里已经问过,闸门就不会再问第二遍。区别只在于那一轮还在跑:除非你写了修改意见,不同意和 ESC 都会把这一轮就地中断(和权限框里点「拒绝」一样),模型拿不到继续跑的机会。这一层由 planMode.approvalGate 控制(默认开),关掉则退出 plan 模式回到只靠 exitPlanModeShift+Tab

默认放行 vs 默认询问

  • 默认放行(只读或仅登记元数据的)read/glob/grep(限定在工作区内)、webfetchwebsearchaskUserQuestionlsploadSkillcreateSubAgentkillBackgroundtodo 全套task 全套(含写操作)、cron 全套cronCreate/cronList/cronDelete)、enterPlanMode/exitPlanMode。task/cron 的写只改自己的清单 / 排程,payload 真正动手时仍走权限门;plan 两件套同理 —— 进只收权,出自带一道确认。
  • 默认询问(会改文件 / 跑命令的)writeeditbash(前台和 run_in_background 一视同仁) —— 落到 defaultEffect(默认 ask)。
  • permissions.deny(裸工具名数组)更硬一档:列进去的工具启动时从注册表摘除,模型看不到也调不了(区别于 ruleseffect: "deny" —— 后者仍把工具报给模型、只在调用时拒)。系统提示里与这些工具绑定的那段说明也会一并消失 —— 摘掉 loadSkill 就不再注入 skills 索引,摘掉 todo 那套就不再讲清单纪律。模型不会被教着去用一个它没有的工具。

读操作被限定在工作区

read/glob/grep 的路径在裁决前会被规范化(resolve + realpath),再检查是否落在「工作区根」内:

  • 工作区根 = --cwd(或当前目录)+ permissions.additionalDirectories 里列出的目录。
  • .. 穿越、符号链接逃逸都会被折算成真实路径后再判断,因此 ../../etc/passwdlink→/etc 这类逃逸会被正确拦下。
  • glob/grep 不带 path 时自动注入工作区根,所以无参搜索默认就在工作区内。

交互式审批

选项快捷键效果
Allow oncey只放行这一次,下次同样的调用还会再问
Denyn / Esc拒绝本次调用
Always allow this toola本 session 内该工具今后都放行(仅存内存,不写盘)

自定义权限规则

~/.nova/nova.config.jsonpermissions.rules 里写规则数组,每条形如 { tool, effect, match? }

  • tool:工具名,或 "*" 匹配任意工具
  • effectallow / deny / ask
  • match(可选):对输入字段的匹配条件,支持精确值 { "command": "ls" }正则(用 /.../ 包裹){ "command": "/^npm test/" }路径包含 { "path": { "within": [...] } }

示例:放行所有 npm test、拒绝带 --data 的 curl、其余一律拒绝:

jsonc
{
  "permissions": {
    "defaultEffect": "ask",
    "additionalDirectories": ["/home/user/shared"],
    "rules": [
      { "tool": "bash", "effect": "allow", "match": { "command": "/^npm test/" } },
      { "tool": "bash", "effect": "deny",  "match": { "command": "/curl.*--data/" } },
      { "tool": "*",    "effect": "deny" }
    ]
  }
}
11

命令沙箱

在权限引擎之上再叠一层 OS 级纵深防御:把会起子进程的工具(bash,前台和后台两条路径都算)放进操作系统沙箱里跑,把文件写入限制在工作区根内。底层是 @anthropic-ai/sandbox-runtime:macOS 用 Seatbelt(sandbox-exec),Linux 用 bubblewrap。

  • 默认关闭(opt-in)。需显式设 sandbox.enabled: true 才开启(或会话内 /sandbox on)。开启后读放行;网络默认不限制(只管文件系统),但可选 network.allowedDomains / deniedDomains 收紧出站连接。
  • 自动降级安全。仅 macOS / Linux 支持;不支持的平台或缺依赖(macOS 需 ripgrep;Linux 还需 bubblewrap/socat)会静默降级为不沙箱,agent 照常运行。
  • 常见缓存默认放行。npm/pnpm/yarn/cargo/rustup/go/pip 等工具链缓存目录(~/.npm~/.cache~/Library/Caches~/.cargo~/.rustup~/go~/.local/share/pnpm~/Library/pnpm~/.yarn)、以及 ~/.config/gh(gh/PR 工作流下 token 刷新)已预置进白名单。显式设置 filesystem.allowWrite替换这组默认值。
  • SDK 强制保护的危险路径。即使在工作区里也写不了:.git/hooks.git/config.vscode/.idea/.claude/{commands,agents},以及 .gitconfig/.zshrc/.mcp.json 等 dotfile。
受保护路径只有一条豁免

这些路径中只有 .git/config 能通过 allowGitConfig(默认 true)放行(git config --localgit remote set-url 需要它);.git/hooks 始终拦。要写其它被保护路径,只能整个关掉沙箱。

配置示例:

jsonc
{
  "sandbox": {
    "enabled": true,
    "monitorViolations": true,        // 捕获越权写并标注到命令输出(macOS 起一个 log 监听)
    "filesystem": {
      "allowWrite": ["~/.npm", "~/.cargo", "/some/extra/dir"],  // 显式设置会替换默认缓存白名单
      "denyWrite": [".env"],           // 即使在允许根内也拒绝写
      "denyRead": ["~/.ssh"],          // 读默认放行,这里单独拒绝
      "allowGitConfig": true
    }
  }
}

如果某个命令要写到工作区外的目录被拦,把对应路径加进 filesystem.allowWrite 即可。

12

上下文管理与压缩

历史是 append-only 的:每轮只往后追加新消息,从不改写更早的内容 —— 这既让持久化只做追加写,也让 DeepSeek 的自动上下文缓存前缀能持续存活。

max_tokens 截断自动续传:

  • 当模型因 stop_reason: "max_tokens" 被截断且没有未完成的工具调用时,loop 会自动重新提示模型从断点继续 —— 长回复撞上该档 maxTokens 时不再半途而废。默认最多连续重试 3 次maxTokensContinuations: 3),每次有实质性进展时计数器重置。设为 0 则回到旧行为:首次截断即硬停止。

auto 压缩(默认开):

  • 只在上下文窗口吃紧时触发。它不截断历史,而是往 append-only 历史里追加一条 <compacted> 摘要边界 —— 完整历史仍留在磁盘、TUI 里照旧全量渲染,只有喂给模型的视图缩短到「最后一条边界往后」。这是一次有意为之的「前缀重置」:边界之内前缀依旧稳定命中缓存,边界推进时才重置一次。可调 compact.auto.enabled / thresholdTokens / contextWindowPercent / maxSummaryTokens
  • 触发阈值默认是上下文窗口的 90%contextWindowPercent,或用 thresholdTokens 钉死一个绝对值)。这个 90% 算的是整个请求 —— system 提示词、记忆、skills 索引、工具 schema,加上对话消息,和 /context 面板显示的口径一致。固定开销通常在一万多 token,窗口越小占比越高,所以把它计入触发判断是必要的:只按消息量算的话,128k 窗口下等阈值触发时真实请求已经超出窗口了。

手动压缩:随时 /compact,可附带关注点(如 /compact 保留关于鉴权的部分)让摘要更聚焦。

缓存计量

每个响应的 cache_read_input_tokens / cache_creation_input_tokens 都会累加进本 session 的用量统计,状态行能看到每轮有多少命中了缓存。用 /usage 看累计值,用 /context 看下一轮请求的即时内存快照。

13

记忆(Memory)

Nova 会像 CLAUDE.md 那样,把项目与用户级的记忆文件注入 system prompt。优先级与查找规则:

  • 每个目录内,按 NOVA.md > CLAUDE.md > AGENTS.md最高优先级的那一个 —— 文件不合并
  • 项目层:从 cwd 向上递归到仓库根,每层各取一个。
  • 用户层:依次找 ~/.nova/NOVA.md~/.claude/CLAUDE.md~/.config/agents/AGENTS.md,取第一个存在的。

文件名可通过 settings.memory.filenames 自定义;用户层 / 全局路径可用 memory.userPaths / memory.globalPath 覆盖。

实战建议

把项目的构建 / 测试命令、架构约定、风格偏好写进仓库根的 CLAUDE.md(或 NOVA.md),Nova 在该仓库工作时会自动带上。

自动记忆(agent 自维护,跨会话)

除了你手写的记忆文件,Nova 还维护一层 auto 记忆 —— agent 自己在工作中沉淀下来的事实,跨会话保留:

  • 落在全局用户目录、按项目分目录:~/.nova/projects/<项目路径编码>/memory/(与 Claude Code 一致;可用 memory.auto.dir 改成工作区内路径)。里面是一个 MEMORY.md 索引,加上每条事实一个文件
  • 索引MEMORY.md,每条一行)注入 system prompt,占很少 token;单条事实正文由 agent 按需 read。注入的索引条数上限 memory.auto.maxEntries(默认 100)。
  • 这个目录归 agent 所有:其中的 read/write/edit 默认放行、不弹权限(见 §10)—— 沉淀一条学到的事实不该每次都问你。
  • 默认放在全局用户目录、按项目标识分目录:记忆随项目走但不污染仓库;若想改为随项目 git 跟踪,把 memory.auto.dir 设为工作区内相对路径(如 .nova/memory)即可。用 memory.auto.enabled: false 关闭整层。
14

Skills

Skill 是「按需加载的专长说明书」。把 SKILL.md 放在:

  • 项目层:.nova/skills/<name>/(兼容 .claude/skills/<name>/
  • 用户层:~/.nova/skills/<name>/(兼容 ~/.claude/skills/<name>/

启动时 Nova 扫描这些目录,把每个 skill 的 name + description 索引注入 system prompt(只占很少 token),并暴露 loadSkill 工具。当某个任务匹配到某个 skill 时,模型才用 loadSkill 拉取完整正文。索引与工具是绑在一起的:skills.enabled: false、一个 skill 都没扫到、或把 loadSkill 写进 permissions.deny,索引和工具都会同时消失,不会出现「提示里列着技能、却没有工具去加载」的情况。

  • /skills 查看发现了哪些、各自来自哪里。
  • 索引预算:默认取当前模型上下文窗口的 1%(按 4 字节 / token 折算),skills.indexBudgetFraction 可调。200k 窗口约 8000 字节,1M 窗口自动放大到 40000。想钉死一个绝对值就设 skills.maxIndexBytes,它优先于比例。
  • 索引里单条描述的上限:skills.maxDescriptionBytes(默认 1536 字节,超出以 标记)。这只影响索引条目,SKILL.md 里的完整 description 不受影响。
  • 超预算时不会丢技能,而是把条目降级成只剩名字(- name),按代价从小到大尽量多保留描述。名字还在就仍然能被 loadSkill 调起,而完整正文本来就要靠 loadSkill 拉。
  • 响应大小上限:skills.maxResponseBytes(默认 16KB)。
  • 单个 SKILL.md 的磁盘上限:skills.maxFileBytes(默认 1MB)。超限的文件在 stat 阶段就被跳过并 warn,不会读进内存。
  • 技能目录可以是符号链接(用来在多个 checkout 之间共享同一份技能),会被正常跟随;链接指向非目录时静默忽略。

Front-matter:只有三件事会让一个 SKILL.md 被拒收 —— 缺 --- 块、缺 name(必须匹配 ^[a-z][a-z0-9-]*$)、缺 description。Nova 认得的字段:

字段作用
name技能名,同时是 /{name} 命令名
description模型据此判断何时该用;原样注入,不截断
when_to_use补充触发条件,拼接到 description 之后
disable-model-invocationtrue 时不进模型索引,只能用户 /{name} 手动调用
user-invocablefalse 时不注册 /{name},只能模型自主调用

其余字段(allowed-toolshooksmodel 等)会被解析后忽略,不会导致加载失败 —— 这样为其它 agent 运行时写的技能可以原样放进来。front-matter 走标准 YAML 子集:嵌套映射、块序列、[a, b] / {a: 1} 流式集合、| / > 块标量、折行的多行标量、# 注释都支持。

两条调用路径:

谁调的怎么走参数
模型自己匹配到索引里的 description → 调 loadSkill 工具 → 拿到展开后的正文无(工具只收技能名)
你敲 /{name} 参数直接读盘、展开、作为下一轮 prompt 注入(一跳,不经过工具)$ARGUMENTS / $1..$N 绑定你敲的内容

两条路都经由同一个渲染函数,模型看到的文本逐字节一致 —— 一个技能不会因为「谁调它」而表现不同。/{name} 每次调用都重新读盘,所以改完 SKILL.md 直接再敲一次就生效,不用 /commands reload(那个只在增删技能目录时才需要)。

正文里的变量与插值:按顺序做四层展开,和自定义 slash 命令用的是同一套实现。

写法展开成
${CLAUDE_SKILL_DIR} / ${NOVA_SKILL_DIR}该技能自己的目录绝对路径
${CLAUDE_PROJECT_DIR} / ${NOVA_PROJECT_DIR}当前工作区根目录
${CLAUDE_PLUGIN_ROOT} / ${NOVA_PLUGIN_ROOT}技能所属插件的根目录(仅插件提供的技能有)
${CLAUDE_SESSION_ID} / ${NOVA_SESSION_ID}当前会话 ID
${CLAUDE_EFFORT} / ${NOVA_EFFORT}当前思考等级(off/low/medium/high/max),随 /effort 变化
$ARGUMENTS / $ARGUMENTS[n] / $1..$N你在 /{name} 后面敲的内容;正文没写占位符(或只绑走了一部分)时,剩下的会以 ARGUMENTS: ... 追加到末尾,不会丢;模型走 loadSkill 时这些占位符原样保留(没有参数可绑)
@相对路径内嵌该文件内容(上限 100KB,超出截断);解析不到文件就原样保留,所以邮箱和 @scope/pkg 安全
!`命令`执行并内嵌输出,走 bash 工具和沙箱;设 skills.disableShellExecution: true 后替换为一行提示且不执行

不认识的 ${NAME} 原样保留(别的工具的变量不会被抹成空),小写的 ${name} 完全不动(JS 模板字符串示例不会被误伤)。取不到值的变量(非插件技能的 ${CLAUDE_PLUGIN_ROOT}、无会话时的 ${CLAUDE_SESSION_ID})同样保持原样而不是变空,这样「不适用」和「解析成空」能区分开。展开顺序是变量 → 参数 → @!,每层的产物喂给下一层,所以 @${NOVA_SKILL_DIR}/ref.md!`grep $1 file` 都能用。

15

自定义 Slash 命令

除了内置命令,你可以用 .md 文件定义自己的 slash 命令:

  • 项目层.nova/commands/(兼容 .claude/commands/.commands/
  • 用户层~/.nova/commands/(兼容 ~/.claude/commands/

规则:

  • 每个 *.md 文件名即命令名(deploy.md/deploy)。
  • 文件前置 frontmatter 声明 description / arg hint / 参数;正文做占位符替换后,作为下一轮 prompt 发给模型。
  • 正文支持的参数写法:{{name}} / {{name|默认值}}(Nova 原生)、$ARGUMENTS$ARGUMENTS[n]0 起)、$1..$N1 起$1 是第一个)、$name(取 args: 声明的具名参数,和 {{name}} 同源同值)、\$ 转义。另外还有 @路径 内嵌文件和 !`命令` 插值。
  • 没被占位符消耗掉的参数会以 ARGUMENTS: ... 追加到末尾,而不是被丢掉——按 token 逐个算账:正文只写了 $1、你敲了两个词,第二个词照样会追加上去。({{name}} 声明式参数例外:声明列表总会吃掉全部参数,所以只要 {{}} 命中过就不再追加。)
  • 优先级:内置命令永远赢;项目层覆盖用户层(同名时)。
  • 改了文件后用 /commands reload 重新扫盘,/commands(或 /help)查看当前注册了哪些。

通过 settings.slash 可调整开关与额外目录(projectDirs / userPaths / extraDirs)。

16

MCP 外部工具

Nova 可在启动时连接外部 MCP 服务器,把它们的工具以 mcp__<服务器>__<工具> 的形式暴露给模型,并走正常权限引擎(默认 ask)。服务器原生的 JSON Schema 原样转发,工具契约不变。

支持两种传输:本地子进程走 stdio,远程端点走 http / sse。在 ~/.nova/nova.config.jsonmcp.servers 下配置:

jsonc
{
  "mcp": {
    "enabled": true,            // 总开关(默认 true)
    "timeoutMs": 60000,         // 单次工具调用超时
    "servers": {
      "filesystem": {           // stdio(type 默认 "stdio")
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
        "env": { "FOO": "bar" }
      },
      "remote": {               // http / sse
        "type": "http",
        "url": "https://example.com/mcp",
        "headers": { "authorization": "Bearer …" }
      },
      "scratch": { "command": "…", "enabled": false }   // 单独跳过某个服务器
    }
  }
}

各服务器并行连接;某个连不上只会记日志并跳过 —— 不阻塞启动、不影响其它服务器。/mcp 打开一个菜单(认证 / 重连 / 登出,查看状态和工具数),/mcp tools 列出所有桥接的工具名;shell 里也有 nova mcp 子命令。

OAuth(远程服务器鉴权)

对以 401/403 挑战的远程 http/sse 服务器,Nova 支持 OAuth 2.0(authorization-code + PKCE)

  • 给该服务器加一个 oauth: {} 块(可选 scope)即启用;mcp.oauth.autoDetect(默认 true)还会把任何 401/403 的远程服务器自动标记为「需认证」,即便没写 oauth 块(用静态 Authorization 头的服务器豁免)。
  • 首次在 /mcp 菜单里选 Authenticate 会打开浏览器走授权;回调由固定的本地端口接收(mcp.oauth.callbackHost/callbackPort,默认 127.0.0.1:7777)。
  • token 持久化在 ~/.nova/mcp-auth/,之后的会话静默刷新,无需再次登录。
17

LSP 代码智能

lsp 工具让模型直连语言服务器(JSON-RPC over stdio),拿到比 grep 精确得多的导航 —— 它懂作用域和类型。一个工具,六个 action:

action作用必需参数
definition跳到定义path + line(+character,默认 1)
references找所有引用同上(include_declaration 默认含声明)
hover某位置的类型 / 文档同上
diagnostics某文件的错误 / 警告path
document_symbols单文件符号大纲path
workspace_symbol按名字跨项目搜符号symbol
  • 坐标对模型是 1-based(行、列),内部自动转成 LSP 的 0-based。
  • 工具只读,权限引擎默认放行。

Nova 不安装语言服务器 —— 它们必须已在 PATH 上。内置自动识别四种(缺失则该语言的调用静默降级为「未安装」提示)。

languageId命令扩展名
typescripttypescript-language-server --stdiots/tsx/mts/cts/js/jsx/mjs/cjs
pythonpyright-langserver --stdiopy/pyi
gogoplsgo
rustrust-analyzerrs

语言服务器在首次 lsp 调用时按需懒启动,所以「已安装但未启动」是正常状态。用 /lsp 查看每种语言:二进制是否在 PATH(● running / ○ installed / ● not installed)以及本 session 是否已起。

在配置 lsp.servers 里可覆盖 / 扩展内置表(按 languageId 匹配;同名整条替换,未知则追加),还可调三个超时:initTimeoutMs / requestTimeoutMs / diagnosticsTimeoutMs

18

插件(Plugins)

插件把可复用的扩展打包成「一个目录 + 一份 manifest」,一条命令即可安装、启停、分发。纯声明式 —— 不执行任何插件代码,只是把目录里的扩展登记进来。格式兼容 Claude Code 插件

Manifest 位于 .nova-plugin/plugin.json(优先)或 .claude-plugin/plugin.json(回退,二者不合并)。一个插件可同时贡献:

  • slash 命令、子 agent、skills、生命周期 hooks —— 与你手写的 .md 扩展(§14/§15)同源,只是随插件一起分发;命令 / agent 名以 <插件名>:<名字> 命名空间化。
  • MCP servers、LSP servers —— 桥接进 §16/§17 的同一套机制。
  • bin/ 可执行文件 —— 其目录被加进 PATH,供 bash(及沙箱)调用。

nova plugin 子命令从 shell 管理(它编辑 ~/.nova/nova.config.jsonplugins 块):

命令作用
nova plugin install <来源>从本地路径、GitHub 仓库、git URL 或 marketplace 安装
nova plugin uninstall <name>卸载一个插件
nova plugin list列出已加载的插件及各自的贡献
nova plugin enable / disable <name>启用 / 停用(停用不卸载)
nova plugin marketplace add <来源>注册一个 marketplace(插件目录),来源同 install
nova plugin marketplace list / remove <name>列出 / 移除已注册的 marketplace

整个插件子系统默认关闭plugins.enabled 默认 false)—— 即便装了插件,也要设 plugins.enabled: true 才会加载。REPL 内的 /plugin 只用于查看已加载插件及其贡献;安装 / 启停等改配置的操作走 nova plugin CLI。插件状态(installed / marketplaces / enabled / disabled)都落在 nova.config.jsonplugins 块;已安装插件缓存在 ~/.nova/plugins/cache,扫描目录默认含 .nova/plugins~/.nova/plugins(及 .claude/plugins 兼容路径)。项目级插件遮蔽同名的用户级插件(首次出现者胜)。

19

会话、检查点与数据落盘

恢复与切换

  • nova -c / nova --continue:恢复最近一个 session。
  • nova --resume <id>:按 id 恢复。
  • REPL 内 /resume [<id>]:切到指定 session(不带参数则弹交互式列表选)。

回退(Rewind)

/rewind [<n>] 回到此前某条消息 —— 其后的对话历史与文件改动都会被丢弃,相当于一个检查点回滚。

自动清理

启动时 Nova 会删掉最近活动超过 sessionCleanup.maxAgeDays(默认 30 天)的 session 目录(按文件最新 mtime 算「最后一次使用」,不是创建时间;当前活动 session 始终受保护)。设 sessionCleanup.enabled: false 可永久保留。

版本更新

  • 启动检查(只提醒,不安装):交互启动时(限流地)比对 npm 是否有新版,有则提示 —— 从不自动安装。节流状态记在 ~/.nova/update-check.json,间隔 update.checkIntervalHours(默认 24h);设 update.enabled: false 静音。
  • 手动升级nova upgradeupdate.command(默认 npm install -g @asathinkeroops/nova-code@latest --registry https://registry.npmjs.org,可改 pnpm/yarn/bun 全局安装)。默认命令显式指定 npmjs 官方源,与版本检测查询的 registry 一致——如果你的 npm 配置了未同步的镜像(npm config get registry 可查),裸命令会"成功"装回旧版。安装后 nova upgrade 会校验磁盘上的实际版本,装到旧版时会明确报错而不是宣称成功。nova --version 打印当前版本。

数据落在哪

内容路径
全局配置~/.nova/nova.config.json
历史 session~/.nova/sessions/{id}/
transcript(hook 事件流)~/.nova/sessions/{id}/transcript.jsonl
可重放 message 历史~/.nova/sessions/{id}/messages.jsonl
子 agent transcript/message~/.nova/sessions/{id}/subagents/
session 日志~/.nova/sessions/{id}/session.log
定时调度条目(cron/loop)~/.nova/sessions/{id}/cron/{id}.json
持久化 Task工作区内 .tasks/{id}.json
自动记忆(agent 自维护)~/.nova/projects/<项目路径编码>/memory/MEMORY.md + 每条一文件;memory.auto.dir 可改回工作区内)
MCP OAuth token~/.nova/mcp-auth/
更新检查节流状态~/.nova/update-check.json
跨会话 token 累计(状态行「累计」命中率)~/.nova/usage.json

--no-transcript 让本次不写 transcript;transcript.enabled: false 全局关闭。

20

配置文件完整参考

配置文件位于 ~/.nova/nova.config.json,是一份 JSON。下面列出全部字段及默认值(来自 zod schema,每个可配项都有默认值,缺省即用默认)。

顶层与模型

字段默认说明
apiKey(无)provider API key(首次向导会写入)。环境变量 NOVA_API_KEY 优先于此项:设了就用它,配置文件里的值作为兜底。想把 key 留在环境里、不落到明文配置文件时用这个
provider"deepseek"驱动 thinking 参数、错误翻译、重试策略的 provider profile(供应商适配):deepseek(effort 旋钮 + 错误翻译 + 状态码重试,默认走 Anthropic 端点,可用 transport 切到 OpenAI 端点)/ moonshot / other(通用 Anthropic 兼容端点,用 budget_tokens)。OpenAI 兼容端点不设独立 provider —— 用 transport: "openai" 在供应商 profile 上切换。未知 id 回退到 other
transport(无)传输协议,与 provider 正交:"anthropic"(@anthropic-ai/sdk 的 Messages 格式)/ "openai"(OpenAI 兼容 chat/completions,官方 openai SDK)。省略 → 用 provider profile 的默认(内置 profile 均默认 anthropic)。一家供应商两个端点(DeepSeek)时用这个切换,DeepSeek 适配原样保留;thinking 旋钮随协议变化(Anthropic 端点用 output_config.effort,OpenAI 端点用 thinking.type 开关 + reasoning_effort 三档强度)
model"pro"当前档位models 表中的 key(lite/pro/max),永远不是裸模型 id
models{}命名的模型档位表,value 为档位对象,每档带自己的 idmaxTokenscontextWindowSizethinkingmodalitiespricing、可选 description。非空时必须含 lite/pro/max 三档(schema 强制)。默认表按 provider 内置在代码里、加载时层叠进来(不写进配置文件,见 §3);这里只写覆盖项
baseURL(无)模型端点 URL,格式随协议:Anthropic 兼容端点(如 DeepSeek 的 https://api.deepseek.com/anthropicdeepseek/moonshot/other 缺省用 SDK 默认端点)或 OpenAI 兼容端点根(如 DeepSeek 的 https://api.deepseek.com,SDK 自动拼 /chat/completionstransport: "openai" 时必须给)
headers(无)附加在每个模型请求上的 HTTP 头,形如 {"User-Agent": "nova/1.0", "X-Tenant": "acme"}。并入 SDK 默认头,同名以这里为准(authorization / x-api-key 也可覆盖,供网关用非标准鉴权头);只作用于模型端点,余额探测 / MCP / websearch 各有自己的传输层。头名按 HTTP token 校验、头值不允许 CR/LF,写错在加载配置时报错
sessionDir~/.nova/sessionssession 存放目录
language"auto"模型回复语言(注入 system prompt),同时也是 TUI 界面语言的默认来源;auto 跟随系统 locale($LC_ALL/$LANG/$LANGUAGE,macOS 还读 AppleLocale),否则填 BCP-47 标签如 en/zh-CN。加载时 auto 会被解析成具体标签
locale"auto"仅 TUI 静态文案的语言覆盖(菜单/提示/状态行);auto = 跟随 language。内置 zh-CN 与 EN,其它标签回落英文。两者可不同(中文界面 + 英文回复),见 §5
maxTokensContinuations3max_tokens 截断且无工具调用时,连续重提示模型继续的最多次数;0 禁用(硬停止)。每次有进展时计数器重置
maxTurns5000单条消息内模型调用轮次上限(模型→工具→模型…直到给出回答);会话总轮数不受限,每条新消息各自有独立配额。触顶不丢工作:注入一条「禁用工具、立即收尾」的请求让模型基于已收集信息作答。默认值很高,单任务连续工作数小时也不会撞
toolConcurrency3单轮内工具并发上限(1 = 全串行)

每档输出上限 / 上下文窗口是 per-tier 的:写在 models.<tier>.maxTokens(schema 缺省 32768,DeepSeek 内置默认三档均为 393216 = 384×1024)和 models.<tier>.contextWindowSize(缺省 1048576 = 1024×1024),不再是顶层字段。models.<tier>.thinking 让同一模型 id 拉出 lite/pro/max 能力梯度(见 §7);models.<tier>.pricing 提供 /usage 成本估算单价。

permissions

字段默认说明
defaultEffect"ask"无规则命中时的兜底(allow/deny/ask
rules[]规则数组(首个匹配生效),见 §10
deny[]裸工具名黑名单:启动时从注册表摘除,模型看不到也调不了(比 rulesdeny 更硬),系统提示里与之绑定的说明同步消失,见 §10
additionalDirectories[]工作区之外、读工具可免询问触及的目录
autoMode.llmClassifiertrueauto 模式下把规则判不定的命令交给 LLM 风险分类器;关掉则一律弹确认
autoMode.model(无→便宜档)分类器用的模型(裸 id 或档位名),独立于 /model
autoMode.classifierTimeoutMs8000分类器超时;超时按「有风险」处理(弹确认,不静默执行)

planMode(plan 模式的进出)

字段默认说明
agentToolstrue注册 enterPlanMode / exitPlanMode,让模型自己切进只读 plan 模式、方案获批后再退出;关掉则 plan 模式只能手动进(Shift+Tab / --permission-mode plan),见 §10
approvalGatetrue一轮结束时若仍在 plan 模式,由 nova 自己弹确认框:同意就恢复进入前的权限档并立刻续跑实现。不依赖模型调 exitPlanMode;关掉则退出全靠模型自觉或你手动 Shift+Tab

trust(工作区信任)

字段默认说明
trust.enabledtrue启动时确认工作区可访问;false 恢复「启动即信任」,见 §10
trust.trustedRoots[]已信任的绝对路径(授权时自动追加,也可手工预置);工作区位于任一根之下即可信

思考等级(thinking)

没有顶层 thinking 配置项 —— 思考等级是 per-tier 的,写在 models.<tier>.thinkingoff/low/medium/high/max;缺省回退 max)。-t/--think/effort 是会话内覆盖,见 §7

compact

字段默认说明
auto.enabledtrue上下文吃紧时自动压缩(追加 <compacted> 边界,见 §12
auto.contextWindowPercent0.9触发阈值占上下文窗口的比例,按整个请求计(含 system / 工具 schema,与 /context 同口径)
auto.thresholdTokens / maxSummaryTokens内置常量绝对阈值覆写(优先于比例)/ 摘要长度上限

invariants(工具不变量,dispatcher 强制)

字段默认说明
enabledtrue总开关
readBeforeEdittrue编辑前必须先读过该文件
mtimeChecktrue检测文件被外部改动(mtime 漂移)

界面与体验

字段默认说明
stream.enabledtrueTUI 里实时流式渲染文本 / 推理(仅配置项,无 slash 命令)
predict.enabledtrue下一条输入预测(/predict 切换)
predict.timeoutMs8000预测超时
predict.maxChars300预测占位最大字符数
todo.autoClearDelayMs2500一张 todo 清单全部完成后自动清空前的停留时长(0 = 不自动清,交给模型自己调 clearTodoList
task.autoClearDelayMs2500同上,但针对落盘的 Task 计划 —— 全部完成后连同 .tasks/ 文件一起删(0 关闭)
terminal.syncOutput / cursorFollowtrue / true同步输出(防闪烁)/ 光标跟随输入框(IME 定位)
logging.level"info"tracefatal
logging.prettytruepretty 日志(--no-pretty 关)

pricing — 成本估算

字段默认说明
pricing.enabledtrue开关 /usage 与状态行的成本估算

单价本身是 per-tier 的:写在 models.<tier>.pricinginput/output/cacheRead/cacheWrite 每百万 token,currencyUSD$CNY¥)。用当前档位自己的费率算钱;某档没写 pricing 就只显示 token、不显示金额。

background / queue — 后台命令与输入队列

字段默认说明
websearch.*websearch 工具的搜索商 key:braveApiKey / tavilyApiKey / serperApiKey,配一个即可(按此顺序自动选);对应环境变量 BRAVE_SEARCH_API_KEY / TAVILY_API_KEY / SERPER_API_KEY 优先于配置文件里的同名 key(与 apiKey 规则一致)
background.autoContinueOnCompletetrue后台命令跑完且 agent 空闲时,自动唤起一轮让它处理结果
queue.consumeInLooptrue回合运行中新键入的普通 prompt 在 loop 边界即时折入(/! 行仍排队)

hooks — Shell 钩子

字段默认说明
hooks.enabledtrue总开关
hooks.PreToolUse[]工具调用前钩子;非零退出即拒绝该调用。每条命令含 matcher(可选正则匹配工具名)、commandtimeout_ms(默认 60s)
hooks.PostToolUse[]工具调用后钩子;stdout 追加到工具结果
hooks.UserPromptSubmit[]用户输入提交前钩子;stdout 作为附加上下文注入
hooks.Stop[]回合结束钩子(仅副作用,不改变行为)
hooks.SessionStart[]会话启动钩子;matcher 取值 startup / resume / clear
hooks.SessionEnd[]会话结束钩子;matcher 取值 exit
hooks.PreCompact[]压缩前钩子;matcher 取值 auto / manual;可通过返回的 JSON 注入压缩指导
hooks.PostCompact[]压缩后钩子;matcher 取值 auto / manual

持久化

字段默认说明
transcript.enabledtrue写 transcript(--no-transcript 临时关)
sessionCleanup.enabledtrue启动时清理旧 session
sessionCleanup.maxAgeDays30旧 session 的天数阈值
update.enabledtrue启动时检查 npm 新版并提醒(从不自动装),见 §19
update.checkIntervalHours24更新检查节流间隔
update.commandnpm install -g @asathinkeroops/nova-code@latest --registry https://registry.npmjs.orgnova upgrade 跑的安装器(可改 pnpm/yarn/bun;默认显式走 npmjs 官方源,避免镜像滞后装回旧版)

扩展子系统

字段默认说明
memory.filenames["NOVA.md","CLAUDE.md","AGENTS.md"]记忆文件名优先级,见 §13
memory.userPaths / globalPath(无)覆盖用户层 / 全局记忆路径
memory.auto.*enabled:true自动记忆(agent 自维护):默认 ~/.nova/projects/<项目编码>/memory/dir 未设,可设为工作区内路径覆盖)、maxEntries=100,见 §13
slash.enabledtrue自定义 slash 命令开关;可覆写的发现目录:projectDirsuserPathsextraDirs
skills.enabledtrueSkills 开关;indexBudgetFraction=0.01、maxDescriptionBytes=1536、maxResponseBytes=16384、maxFileBytes=1048576、disableShellExecution=false;maxIndexBytes 可选(钉死索引预算,优先于比例);可覆写的发现目录:projectDirsuserPathsextraDirs
subagent.enabledtrue子 agent 开关;model(按子 agent 名索引的档位表,见 §8)/maxTurns=5000/maxTokens=32768;子 agent 的 maxTokensContinuations 继承顶层设置;自定义类型目录:projectDirs/userPaths/extraDirs
guide.*enabled:truenova-code-guide 来源:source=remote(默认,克隆 repoUrl@refcacheDirrefreshIntervalHours=24)或 local(读 localPath/工作区),见 §8
goal.*enabled:true/goal 目标模式:evalModel(判定档位,模板设 lite)/maxContinuations=25/maxEvalTurns=15
loop.*maxIterations:100, minIntervalMs:1000/loop 重复任务:maxIterations 安全上限、minIntervalMs 拒绝过密间隔,见 §6
cron.*enabled:true定时调度工具:maxSchedules=20、minIntervalMs=1000、maxIterations=100(enabled 只管 agent 工具,/loop 不受影响),见 §9
lsp.*enabled:trueLSP,见 §17;超时可配:initTimeoutMs=15000、requestTimeoutMs=15000、diagnosticsTimeoutMs=3000
mcp.*enabled:true, timeoutMs:60000MCP;servers + oauth.*(回调 127.0.0.1:7777autoDetect:true),见 §16
sandbox.*enabled:false, monitorViolations:true命令沙箱(默认关);filesystem.* + network.*(默认不限网,可设 allowedDomains/deniedDomains),见 §11
plugins.*enabled:false插件子系统(默认关);projectDirs/userDirs/disabled/installed/marketplaces,见 §18
临时覆盖

-m/--model-t/--think--max-turns--cwd--no-transcript--no-pretty--permission-mode--dangerously-skip-permissions 只影响本次会话,不写回文件。--output-format 仅在无头模式(-p)下生效。

21

常见问题与排查

在管道 / CI 里能用吗?没有 TTY 会怎样?

能。没有 TTY 时 Nova 不会报错,而是走 headless 模式:跑一轮(prompt 从参数、-p 或 stdin 取)、打印结果、退出。要机器可读输出用 --output-format json|jsonl;无人值守批准配 --dangerously-skip-permissions。全屏 REPL 才需要真正的终端。

启动报 apiKey 未设置

跑一次首启向导填上,或手动编辑 ~/.nova/nova.config.jsonapiKey/baseURL/model;也可以只导出环境变量 NOVA_API_KEY(优先于配置文件里的 apiKey/doctor 会标明当前 key 的来源)。

websearch 报缺 key

~/.nova/nova.config.jsonwebsearch 下填 braveApiKey / tavilyApiKey / serperApiKey 任一项,或设置对应环境变量 BRAVE_SEARCH_API_KEY / TAVILY_API_KEY / SERPER_API_KEY(按 brave → tavily → serper 顺序自动选用;同一家两处都配时环境变量优先,与 apiKey 同一条规则)。

启动时让我确认「是否信任这个文件夹」

这是工作区信任门(见 §10)。选 Yes 会把该目录记进 ~/.nova/nova.config.jsontrust.trustedRoots,以后不再问;对 ~ 授予的信任只在本次会话有效。headless 场景弹不出这张卡片,未信任会直接失败 —— 先交互式信任一次,或手工加进 trust.trustedRoots,或用 --dangerously-skip-permissions

想要中文界面但英文回复(或反过来)

language 管模型回复语言,locale 只管 TUI 静态文案。要中文界面 + 英文回复就写 {"locale": "zh-CN", "language": "en"};两者默认都是 auto(跟随系统 locale)。改完重启生效 —— 回复语言写在 system prompt 里,会话中途改会击穿前缀缓存。

能读 PDF 吗?

能。read 直接吃 .pdf(≤30MB),抽取的文本带行号、每页前有 [Page N] 标记,offset/limit 照常翻页。扫描件 / 纯图片 PDF 抽不出文本,工具会明说并建议改用 ocrmypdf/tesseract 之类先 OCR。

每次写文件 / 跑命令都来问我,太烦

在审批框选 Always allow this tool(本 session 内不再问),或在 permissions.rules 里给具体命令 / 工具加 allow 规则(见 §10)。启动时 --permission-mode acceptEdits 让工作区内的写自动放行,--dangerously-skip-permissions 全自动批准(适合 CI/无人值守)。

某条命令被沙箱拦了写入

把目标路径加进 sandbox.filesystem.allowWrite;若要写 .git/hooks 等 SDK 强制保护路径,只能 sandbox.enabled: false 关掉沙箱。

lsp 总说「未安装」

Nova 不装语言服务器。先把对应二进制装到 PATH(typescript-language-server/pyright-langserver/gopls/rust-analyzer),用 /lsp 确认状态。

感觉缓存没命中、变慢变贵

保持历史前缀稳定 —— 靠 append-only 历史和 auto 压缩(追加 <compacted> 边界、不改写更早内容)各司其职。状态行可看每轮缓存命中量。

想回到几步之前、撤掉刚才的改动

/rewind [<n>] 回退到更早的消息(其后的历史与文件改动会被丢弃)。命令会先展示将受影响的文件供你确认。

长回复被 max_tokens 截断

Nova 的 loop 会自动检测 max_tokens 截断并重新提示模型继续。默认最多连续续传 3 次(maxTokensContinuations: 3),每次有进展时计数器重置。设为 0 可禁用此特性。若长回复频繁被截断,也可以直接调高该档的 models.<tier>.maxTokens(DeepSeek 内置默认已是 393216)。

如何切换模型?

REPL 内用 /model(无参数弹出交互列表),或 /model <tier>(如 /model pro)—— 只接受配置过的档位名,裸模型 id 会被拒绝。CLI 启动时用 -m / --model(同样只认档位名)。模型档位在配置的 models 字段定义,非空时须含 lite/pro/max

怎么升级 Nova?

nova upgrade 跑配置里的安装器升到最新版;交互启动时若有新版也会(限流地)提示。nova --version 看当前版本。想静音提醒设 update.enabled: false

本手册依据当前代码生成。如对内部架构、loop 契约或扩展点感兴趣,请进一步阅读仓库根的 CLAUDE.mdREADME.md

Nova · 终端编码 Agent · 模型层面向国产大模型调优 · 单文件文档