# nikou-cli

`nikou-cli` 是一个面向 Agent 工具链的命令行工具，提供四类能力：

- 安装、列出、删除和创建 Agent Skills。
- 安装、搜索、列出和删除 MCP server 配置。
- 启动 AI Hook 主从节点，把聊天消息分发给 Codex、Claude 或 Gemini 从节点执行。
- 在每个节点独立运行、调度和审计 Nikou Job。
- 让 AI 在飞书卡片上发起抉择，用户点一下按钮即回复，会话原地继续。

> 本仓库不包含任何真实密钥。Hook 主从节点需要你自己准备机器人应用凭证，并写入本机配置文件。

## 安装

```bash
npm install -g nikou-cli
```

也可以直接使用 `npx`：

```bash
npx nikou-cli --help
```

要求 Node.js `>=20`。Windows 使用 Node.js 24 时，依赖的 `better-sqlite3`
会直接下载对应预编译包，不要求用户安装 Visual Studio C++ 或 Windows SDK。

## 命令总览

```bash
nikou-cli --help
nikou-cli add <owner/repo>
nikou-cli list
nikou-cli remove <name>
nikou-cli init <name>
nikou-cli mcp <command>
nikou-cli ask-mcp <command>
nikou-cli hook <command>
nikou-job <command>
```

## Skills

### 从 GitHub 仓库安装 Skill

```bash
nikou-cli add anthropics/skills
```

安装指定子目录：

```bash
nikou-cli add anthropics/skills --path skills/frontend-design
```

安装到指定目录：

```bash
nikou-cli add anthropics/skills --path skills/frontend-design --dest ~/.codex/skills
```

指定分支：

```bash
nikou-cli add anthropics/skills --branch main
```

覆盖已存在的 Skill：

```bash
nikou-cli add anthropics/skills --path skills/frontend-design --force
```

### 查看已安装 Skills

```bash
nikou-cli list
nikou-cli list --dest ~/.codex/skills
```

### 删除 Skill

```bash
nikou-cli remove frontend-design
nikou-cli remove frontend-design --dest ~/.codex/skills
```

### 创建 Skill 模板

```bash
nikou-cli init my-custom-skill
nikou-cli init my-custom-skill --dest ~/.codex/skills
```

生成结构：

```text
my-custom-skill/
  SKILL.md
```

## MCP

`nikou-cli mcp` 会把 MCP server 配置写入常见 Agent 工具的本机配置文件。

支持目标：

- `claude`
- `codex`
- `gemini`
- `kiro`
- `opencode`
- `all`

### 搜索 MCP 资源

```bash
nikou-cli mcp search apifox --api-url https://your-platform.example.com/api/v1
```

### 安装 MCP

```bash
nikou-cli mcp add apifox-mcp-server \
  --api-url https://your-platform.example.com/api/v1 \
  --tools codex,claude
```

指定配置名：

```bash
nikou-cli mcp add apifox-mcp-server \
  --api-url https://your-platform.example.com/api/v1 \
  --name apifox \
  --tools codex
```

强制覆盖已存在配置：

```bash
nikou-cli mcp add apifox-mcp-server \
  --api-url https://your-platform.example.com/api/v1 \
  --tools all \
  --force
```

### 查看已安装 MCP

```bash
nikou-cli mcp list
nikou-cli mcp list --tools codex
```

### 删除 MCP

```bash
nikou-cli mcp remove apifox
nikou-cli mcp remove apifox --tools codex,claude
```

## Nikou Job

`nikou-job` 是与 Hook Worker 解耦的本机 Job daemon。每个安装 `nikou-cli` 的节点维护自己的 Job、revision、运行记录和日志，不通过主节点汇总，也不会因 Worker 离线而停止调度。

默认监听 `127.0.0.1:19734`，数据写入：

```text
~/.nikou-block/jobs/jobs.db
~/.nikou-block/jobs/bundles/
~/.nikou-block/jobs/runs/
~/.nikou-block/logs/job/
~/.nikou-block/state/job-daemon.json
```

前台启动和探活：

```bash
nikou-job daemon
nikou-job health
```

安装为当前用户的常驻服务：

```bash
nikou-job service install
nikou-job service status
nikou-job service restart
```

macOS 使用 LaunchAgent `com.nikou.job`，Linux 使用 user systemd `nikou-job.service`。服务管理目前支持 macOS 和 Linux；Windows 可以前台运行 daemon。

Job daemon 会在每次运行任务时从当前用户环境补齐 CLI 搜索路径：macOS/Linux 读取登录 Shell，Windows 读取用户与系统环境，并兼容 npm 的 `.cmd` 入口。任务子进程只会获得基础运行变量和 Job 显式声明的 `envKeys`，不会透传未声明的环境变量；无需在 Job 定义或服务文件中写死 Codex、Claude、Kiro 等命令的安装路径。

查看和操作 Job：

```bash
nikou-job list
nikou-job show <job-id>
nikou-job runs <job-id> --limit 50
nikou-job logs <run-id> --stream stdout
nikou-job run <job-id>
nikou-job enable <job-id>
nikou-job disable <job-id>
nikou-job event-rules
nikou-job event-rule show <rule-id>
nikou-job event-rule test <rule-id> ./event.json
nikou-job events --limit 100
nikou-job event show <delivery-id>
```

创建或修改必须先生成草案并校验，`apply`、回滚和归档都需要显式 `--yes`：

```bash
nikou-job draft ./job-definition.json
nikou-job validate <draft-id>
nikou-job apply <draft-id> --yes
nikou-job rollback <job-id> <revision> --yes
nikou-job archive <job-id> --yes
```

配置文件默认为 `~/.nikou-block/jobs/config.json`，同时兼容 snake_case 与 camelCase。可配置端口、最大并发、错过调度阈值、保留周期、看板链接和失败通知；密钥只允许来自本机环境或外部工具配置，禁止写入 Job bundle。

Job 定义支持可选的 `notify` 字段，控制妮蔻 Job 自带的运行通知：`true` 或省略时通知运行中、成功、跳过和失败等状态；写入 `false` 时关闭 Job daemon 与 CC Switch 的通用桌面通知，也关闭 Job daemon 的 AI 失败告警。旧 Job 未写该字段时按 `true` 兼容处理。Job 脚本自己发送的业务消息不受影响，例如 UAT ERROR 脚本仍只在查到 ERROR 时发送告警卡片。

```json
{
  "id": "uat-error-log-alert",
  "notify": false
}
```

### 被动事件与 Event Job

妮蔻的被动事件复用主节点现有唯一飞书长连接，不启动第二套订阅进程。主节点只向当前群已绑定且声明订阅的 Worker 定向投递；Worker 再通过本机 loopback API 交给 `nikou-job`。普通群聊问答仍要求 `@妮蔻`，非 `@妮蔻` 消息不会进入原有 AI task 链路。

事件规则采用受限 DSL，将原始平台事件转换为稳定的妮蔻语义事件。规则默认停用，支持 `all`、`any`、`not` 与固定比较/文本/数组操作，不执行任意 JavaScript、shell 表达式或无限制正则。规则试跑是纯计算，不发卡、不 emit，也不创建 Job run。

Job 可以保留旧 `schedule`，也可以声明多个 `triggers`。Event-only Job 的 `nextRunAt` 为 `null`；需要人工确认时可声明 `confirmation`：

```json
{
  "id": "deployment-environment-check",
  "name": "部署咨询环境检测",
  "enabled": false,
  "triggers": [{
    "id": "deployment-status-requested",
    "type": "event",
    "eventType": "nikou.deployment.status-requested.v1",
    "deliveryPolicy": "best_effort"
  }],
  "confirmation": {
    "required": true,
    "title": "是否进行环境检测？",
    "expiresInSeconds": 600,
    "allowedOperators": ["requester", "binding_owner"]
  }
}
```

事件规则仍使用草案门禁，顶层增加 `kind: "event-rule"`；省略时按旧 Job 处理。创建、修改、回滚和归档继续走 draft → validate → 明确确认 → apply。运行时消息正文只写入权限为 `0600` 的 `{{RUN}}/event.json`，不放入命令参数或环境变量。

未命中事件不保存消息正文；命中事件只保存最多 120 字的脱敏预览 24 小时，结构化元数据、规则判定和执行关联保留 30 天。CC Switch 只展示本机 Worker 与本机 Job daemon 的规则和事件流水，不聚合其他从节点。

### 步骤主动跳过（退出码 78）

pipeline 步骤默认只有「成功则继续」「失败则中断」两种结果。当某一步在运行时发现前置条件不成立（例如当天没有需要处理的数据），可以用退出码 `78` 声明「本次无需继续」：

```bash
echo "今日无待处理数据，跳过本次执行"
exit 78
```

runner 收到后会立刻结束本次 pipeline，剩余步骤不再启动，整次 run 状态落 `skipped`：

- 不计入失败统计，也不会触发失败通知。
- 通知与 Job 页显示「已跳过」，`run.summary` 取该步骤最后一行 stdout，即上面的跳过原因。
- 该步骤自身在 `run.metadata.steps` 中记为 `status=skipped`、`exitCode=78`。

只有 `type=script` 的步骤支持这个约定。`type=ai` 步骤的退出码由模型与上游服务决定，返回 `78` 仍按失败处理。

从旧版 `nikou-screen` 迁移时先停止旧 mac-agent，再执行 dry-run。命令只有追加 `--yes` 才会复制数据：

```bash
nikou-job migrate screen
nikou-job migrate screen --yes
```

迁移会备份旧数据库，在 staging 目录改写 bundle/run 路径并校验 SQLite 完整性、外键、记录数和源文件 SHA，最后原子切换到 `~/.nikou-block/jobs`。目标目录已存在或旧数据库仍被占用时会拒绝执行。

## 妮蔻抉择（卡片交互）

AI 需要用户拍板时（例如「要不要部署」「走 A 方案还是 B 方案」），可以调用 MCP 工具
`nikou_ask` 在飞书卡片上给出按钮。用户点一下就等于回复，AI 会话原地拿到结果继续执行，
不需要用户再打字。

两个场景走同一条链路：

| 场景 | 卡片位置 | 目标群 |
| --- | --- | --- |
| 妮蔻托管会话（群聊 / 单聊任务） | 直接嵌在正在流式更新的结果卡片里 | 由 Worker 注入的任务上下文自动定位 |
| 本机自用会话（自己在终端跑 codex / claude） | 单独发一张卡片 | 项目 `ops.conf` 或显式传入 |

### 安装

在主节点生成并取得管理密钥，然后在使用 AI 的机器上安装 MCP：

```bash
nikou-cli ask-mcp install
nikou-cli ask-mcp install --tools codex,claude,kiro --force
nikou-cli ask-mcp status
```

`install` 会把 `nikou_ask` 写入对应工具的本机全局配置，并解析 `nikou-cli` 的绝对路径，
避免 AI CLI 子进程的 `PATH` 不含 npm 全局 bin。也可以用 `--command` 显式指定路径。

支持目标：`codex`、`claude`、`kiro`、`gemini`、`opencode`，默认写入前三个。对应配置文件：

```text
~/.codex/config.toml         [mcp_servers.nikou_ask]
~/.claude.json               mcpServers.nikou_ask
~/.kiro/settings/mcp.json    mcpServers.nikou_ask
```

安装后需要重启对应 AI CLI 才会加载。

### 配置

请求方配置写入 `~/.nikou-block/ask/config.json`，权限 `0600`，首次 `install` 会生成骨架：

```json
{
  "admin_url": "http://203.0.113.10:19733",
  "admin_key": "CHANGE_ME",
  "default_chat_id": ""
}
```

`admin_key` 就是主节点的 `hook.admin_key`，在主节点用 `nikou-cli hook admin-key --show`
取得。也可以用环境变量 `NIKOU_ASK_ADMIN_URL` / `NIKOU_ASK_ADMIN_KEY` 覆盖。
密钥不接受命令行参数，避免出现在进程列表和 shell 历史里。

### 目标群解析顺序

1. 工具调用显式传入的 `target`（`chat_id`）
2. 托管会话环境变量 `NIKOU_TASK_CHAT_ID`（Worker 自动注入）
3. 从当前目录向上查找项目 `ops.conf`，取 `notice.feishu.chat_id`，回退 `project.chat_id`
4. `~/.nikou-block/ask/config.json` 的 `default_chat_id`

四项都拿不到时工具直接报错，并提示 AI 让用户补充目标群，不会随便找个会话发出去。

托管会话下主节点还会以自己的任务记录为准覆盖目标会话，不采信调用方传入的 `chat_id`，
避免 AI 把抉择卡片投递到其它群。

### 行为与边界

- 默认等待 10 分钟，可通过 `timeout_ms` 调整，硬上限 30 分钟（避免撞穿引擎自身的单次执行上限）。
- 抉择支持 2 到 6 个选项，用户点击后按钮全部置灰并显示「已选择 X（操作人：Y）」。
- 超时不等用户点击，主节点会主动把卡片置灰并改成超时文案；此后再点只返回提示，不改变结果。
- 每个抉择带一次性 `action_token`，校验失败或重复点击都只返回结论。
- 群内任意成员都可以点击，卡片会记录实际操作人。
- 任务被「停止」、执行出错或已返回结果时，未完成的抉择会一起收掉，不留下点了没反应的按钮。
- 超时返回给 AI 的结果会明确要求它不要擅自执行有副作用的操作，改为说明进展并等待后续指示。

已知限制：

- `kiro` 走常驻 ACP 进程、`api` 模式的 MCP 由 Worker 进程级管理，这两种引擎无法按任务注入
  会话上下文。托管会话下需要靠项目 `ops.conf` 或显式 `target` 定位群。`codex`、`claude`、
  `gemini` 可自动定位。
- 本机场景不校验目标群白名单，安全边界是 `admin_key` 所在文件的 `0600` 权限；不要把该文件
  复制到不受信的机器。

### 主节点接口

抉择复用主节点管理接口，需要已配置 `hook.admin_key`：

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| POST | `/admin/ask` | 创建抉择并发卡，立即返回 `ask_id` |
| GET | `/admin/ask/:askId?wait=30` | 长轮询结果，最多挂起 30 秒 |
| POST/DELETE | `/admin/ask/:askId/cancel` | 取消抉择 |
| DELETE | `/admin/ask/:askId` | 取消抉择 |

## AI Hook 主从节点

Hook 支持三种启动模式：

- 本机一体：一条命令同时启动主节点和本地从节点，适合单机使用。
- 单独从节点：只启动 Worker，连接已有主节点。
- 一主多从：单独启动一个主节点，再让多台机器的从节点接入。

主节点负责连接聊天平台、接收消息、维护 Worker 列表并分发任务。从节点负责连接主节点，收到任务后调用 Codex、Claude、Gemini、API Agent 或知识库客户端执行。

### Worker 模型与思考深度

Worker 的模型和思考深度不跟随本机原生 CLI 的后台切换：你在本机 `codex`、`claude`、`kiro-cli` 里换模型或换 effort，都不会影响正在运行的 Worker。要调整只能通过飞书菜单 `switch_model` 或 CC Switch 的「妮蔻 · 运行档案」。

模型和思考深度按**单聊、群聊分别保存，切换后无需重启 Worker**，下一条消息即生效：

- 设置按「宿主飞书用户 + 引擎 + 会话类型」保存在主节点 `~/.nikou-block/master/worker-runtime-scopes.json`（权限 `0600`），主节点重启后继续生效。
- 群聊不按 `chat_id` 细分：同一宿主的所有群聊共用一套设置，单聊单独一套。
- 主节点在下发任务时按会话类型附带该设置，Worker 只在本轮执行生效，不修改进程启动参数。任务卡片展示的模型与思考深度就是本轮真实生效的值。
- 未设置的会话类型回落 Worker 启动值；只设置了思考深度时模型仍用启动值，反之同理。
- **换引擎仍需重启**（例如 codex 换 claude），因为一个 Worker 进程只能承载一个引擎；卡片会把按钮文案切换为「切换并重启」。
- 引擎不区分单聊和群聊，模型和思考深度才区分。
- 旧版从节点不支持该能力时，主节点下发的设置会被忽略，菜单仍按原有「切换并重启」方式工作，因此升级时应先部署主节点再升级 Worker。

| 引擎 | `--model` 取值 | `--effort` 落地方式 |
| --- | --- | --- |
| `codex` | Codex 原生模型 ID，如 `gpt-5.6-sol` | 转换为 `-c model_reasoning_effort="<level>"`；Codex 没有 `max`，会按语义降到 `xhigh` |
| `claude` | Claude Code 原生短横线模型 ID，如 `claude-opus-4-8`、`claude-sonnet-4-6` | 直接透传 Claude Code 的 `--effort` |
| `kiro` | `kiro-cli chat --list-models` 中的模型 ID，如 `claude-opus-5`、`auto` | 直接透传 Kiro CLI 的 `--effort` |
| `kimi` | Kimi 原生 ACP 模型别名，如 `kimi-code/k3-256k`、`kiro/claude-opus-5` | 透传为 ACP `thinking`，**可选档位由所选模型决定**，详见下文 |
| `api` | profile 的 `models` 列表项 | 不适用，按 profile 配置 |

思考档位是模型能力，不是全局常量。Codex、Claude、Kiro 恰好共用同一套 `low|medium|high|xhigh|max`（默认 `medium`），非法值会被忽略，不会把脏值透传给底层 CLI。Kimi 的档位则随模型变化，例如：

| Kimi 模型 | 可用档位 |
| --- | --- |
| `kimi-code/k3`、`kimi-code/k3-256k` | `low` / `high` / `max` |
| `kiro/claude-opus-5`、`kiro/gpt-5.6-sol` 等 | `off` / `on` / `high` |
| `kimi-code/kimi-for-coding-highspeed` | `on` / `high` |

因此给 Kimi 传 `--effort medium` 会被明确拒绝。选择顺序统一为「先定模型，再定档位」；换模型后如果原档位在新模型上不可用，会自动落到该模型的默认档位。

```bash
nikou-cli hook worker codex --model gpt-5.6-sol --effort medium
nikou-cli hook worker claude --model claude-opus-4-8 --effort high
nikou-cli hook worker kiro --model claude-opus-5 --effort xhigh
nikou-cli hook worker kimi --model kimi-code/k3-256k --effort high
```

### 查询运行档案可选项

`hook worker-options` 是只读命令，输出某个引擎的可选模型、该模型可用的思考档位和默认值。它不启动 Worker、不写状态文件、不返回任何凭据，供 CC Switch 的「妮蔻 · 运行档案」与飞书菜单共用同一份候选项：

```bash
nikou-cli hook worker-options
nikou-cli hook worker-options --engine kimi
nikou-cli hook worker-options --engine kimi --model kiro/claude-opus-5
nikou-cli hook worker-options --engine codex
```

不传 `--engine` 时只返回引擎列表。`--model` 用于按指定模型解析档位；不传时按该引擎原生当前模型解析。Kimi 会起一次临时 ACP 会话读取并立即删除，耗时约数秒。

Claude 的模型名必须用短横线原生 ID。`claude-opus-4.8` 这类小数点写法会被服务端当成不存在的模型返回 404。

从节点启动卡片和任务结果卡片会展示实际生效的引擎、模型和 effort，可以据此确认参数是否真的固定住了。

Kiro 的 `--model` / `--effort` 是「新建会话时」生效的初始值，当前 Kiro ACP 未提供会话级改模型的能力（`sessionCapabilities` 为空）。妮蔻每轮任务都会新拉起一个 Kiro ACP 子进程并按当前会话类型的设置传入这两个参数，因此切换后**不需要重启 Worker**；但已有话题继续 resume 时，Kiro 仍会沿用该会话创建时的模型。需要立刻用新模型时，在单聊里通过菜单 `chat_new` 或群聊新话题开一个新会话。

### 托管会话的高危工具隔离

Hook 托管的 AI 会话禁止使用 hst-mcp 的 `dbgw` 系列堡垒机工具（生产库直连查询）。约束由能力提供方 hst-mcp 强制生效，不依赖提示词。

Worker 为 AI CLI 子进程注入两个环境变量：

| 环境变量 | 值 | 作用 |
| --- | --- | --- |
| `HST_MCP_HOOK_MANAGED` | `1` | 声明当前进程树属于 hook 托管会话 |
| `HST_MCP_DISABLED_MODULES` | 追加 `dbgw` | 兼容旧版 hst-mcp 的模块屏蔽清单 |

两者都可能被 AI 在 shell 中清除，因此 hst-mcp 侧还会独立按进程祖先链判定托管身份：只要祖先链上存在 `nikou-cli hook worker/local/master` 或兼容入口 `ai-hook`，即无条件禁用 `dbgw`。因此以下方式都无法逃逸：

- 清空或伪造环境变量后再调用。
- 绕过 MCP 协议层，直接 `require` hst-mcp 的能力模块调用 `handleTool`。
- 通过 skill 提供的本地 fallback 脚本调用。

宿主本机在终端里直接使用 `kiro-cli`、`codex`、`claude` 时祖先链没有 hook 进程，`dbgw` 工具照常可用，不受该隔离影响。

需要新增受限模块时，只需在 hst-mcp 的 `src/utils/compliance.js` 扩展禁用清单，`nikou-cli` 无需改动。注意工具屏蔽不等于凭据隔离：托管会话仍可读到本机 `~/ops/hst-mcp-config.json`，如需更强边界应让 Worker 以独立系统用户运行并收紧该文件权限。

### 部署角色与知识库节点

部署前先按角色选择目标宿主机：

| 角色 | 运行内容 | 部署边界 |
| --- | --- | --- |
| 知识库宿主机主节点 | 飞书接入、任务路由、Worker 注册 | 与知识库 Worker 部署在同一台知识库宿主机，不按树莓派处理 |
| 知识库宿主机 Worker | FastGPT knowledge Worker、知识库群聊任务 | 唯一的知识库 Worker，关闭单聊路由 |
| 个人节点 | Kiro/Codex 等个人 Worker | 仅处理对应宿主的单聊和绑定任务 |

当前知识库宿主机同时托管主节点和唯一的知识库 Worker，两个 systemd 服务均配置为开机自启。发布新版本后，在同一台知识库宿主机安装对应版本，并按顺序重启两个服务：

```bash
npm install -g nikou-cli@<version> --force --registry=https://registry.npmjs.org/
nikou-cli --version
systemctl restart com.vangelis.agent-block.hook.master.service
systemctl restart ai-hook-knowledge.service
systemctl is-enabled com.vangelis.agent-block.hook.master.service ai-hook-knowledge.service
systemctl is-active com.vangelis.agent-block.hook.master.service ai-hook-knowledge.service
node -e 'const s=require("/root/.nikou-block/state/worker.json"); console.log({pid:s.pid,status:s.status,engine:s.engine,slaveConnectionStatus:s.slaveConnectionStatus})'
```

验收时应确认两个服务均为 `enabled/active`，知识库 unit 的 `ExecStart` 精确为 `nikou-cli hook worker knowledge`，主节点监听端口和状态文件正常，Worker 使用目标版本、`engine=knowledge`、`slaveConnectionStatus=online`，并在主节点日志中确认该 Worker 的 `p2p_enabled=false`。同时确认主节点上只有这一个知识库 Worker，没有其他引擎的 Worker unit 或进程。FastGPT/知识库平台容器按各自仓库与服务说明单独部署。

群聊发送 `@机器人 绑定: <目录>` 时，主节点会先检查当前用户是否已经绑定该目录；已有绑定会直接返回当前绑定状态卡片。当前用户已有其他目录时，会通过卡片确认“覆盖原绑定”或“新增绑定目录”；覆盖操作会在新目录校验成功后再撤销旧绑定。一个群可以保留多个用户、多个目录的 Agent 绑定，绑定指令不会广播给其他 Worker 抢占。

绑定命令支持空格、半角冒号或全角冒号作为命令分隔符，例如 `绑定  D:\\project\\repo`、`绑定: D:\\project\\repo` 和 `绑定：D:\\project\\repo`。路径内容会原样保留，因此 Windows 目录名中的全角冒号不会被转换。

绑定路径支持 `~` 前缀，例如 `绑定 ~/ops/project`。`~` 只在从节点本机展开（主节点与从节点的 home 目录不是同一个），目录不存在时会同时回显原始输入和展开后的真实路径，便于确认是路径写错还是机器不对。

绑定白名单以主节点 `hook.bind_allowed_user_ids` 为准，支持热加载。白名单发生变化时，主节点会立即通过 `worker_policy_update` 下发给所有在线从节点，从节点不再依赖启动握手时的旧快照，因此加入白名单后无需重启 Worker。旧版从节点不支持该消息时会忽略，仍需重启才能生效。

从节点判定无法处理某个任务时（例如不在白名单、路径不存在、未绑定目录）会回传明确原因。对绑定、单聊这类定向下发的任务，主节点会直接把真实原因回给用户，不再统一显示为“从节点响应超时”；群聊广播任务由多个从节点各自跳过，不会因此打扰用户。旧版从节点不上报该消息时，主节点仍按认领超时兜底。

绑定成功后会返回绿色状态卡片，展示本次绑定、当前群的全部普通目录绑定、完整路径、对应 Agent，以及知识库固定绑定（如有）。卡片底部同时给出省略盘符路径、完整路径和无路径选择三种解绑方式，便于确认当前真实绑定状态。

群聊内任何成员都可以发送解绑指令。路径既支持完整路径，也支持省略盘符后的末两级路径；不带路径时会显示卡片供用户选择。Agent 选择卡片会展示省略盘符路径、在线状态：在线 Agent 带绿色状态标识且可选择，离线 Agent 会置灰禁用；同一个话题首次选择 Agent 后，后续消息会继续定向到该 Agent，不再重复弹出选择卡片；不同话题仍然独立选择。卡片会倒计时 10 秒，超时自动使用当前群最近一次成功选择且仍在线的 Agent；没有可用默认 Agent 时，才会发送置灰的超时状态卡片。卡片底部同时提示解绑方式。

```text
@妮蔻 解绑 D:\project\hst\report
@妮蔻 解绑 hst\report
@妮蔻 解绑
```

省略路径同时命中多条绑定时不会直接删除，而是显示解绑选择卡片，避免误操作。解绑会持久记录撤销状态，Worker 重连不会恢复旧绑定。

主节点管理员可查询或持久移除群聊 Agent。移除会记录撤销标记，Worker 重连时不会用本机旧快照恢复；该用户重新在群里发送绑定指令后，可正常解除撤销并重新绑定。

```bash
nikou-cli hook binding list --chat-name "研发协作群"
nikou-cli hook binding list --chat-id oc_xxx

nikou-cli hook binding remove \
  --chat-name "研发协作群" \
  --agent alice \
  --yes
```

不传 `--dir` 时，会移除该 Agent 在群内的全部目录；如只需移除一条目录绑定，可追加 `--dir /srv/project-a`。群名匹配到多个 `chat_id` 时，命令会停止并要求改用 `--chat-id`，避免误删。

群聊发送 `绑定知识库` 后，任务会定向到配置的知识库 Worker 和固定目录，并返回不暴露知识库目录、IP 或宿主的红色绑定状态卡片。卡片同时展示当前群其他 Agent 目录绑定和解绑指令。发送 `解绑知识库` 可移除当前群的知识库固定绑定。知识库绑定只作用于群聊；单聊仍按发送人的飞书 `user_id` 选择其归属 Worker，同一用户多台 Worker 在线时优先使用最近接入的一台。

### 回复模式快捷指令

单聊发送 `切换回复` 或 `/切换模式`，群聊发送 `@妮蔻 切换回复` 或 `@妮蔻 /切换模式`，主节点会返回回复模式设置卡片。卡片回显当前状态，可在以下模式间反复切换：

- `实时卡片回复`：默认模式。任务认领后立即发送卡片，并持续刷新最新执行过程和结果。
- `最终卡片回复`：执行期间只在主节点收集并限制执行过程，不创建或刷新进度卡片；任务完成、失败、取消或 Worker 离线后，仅发送一张当前结果卡片。

设置按飞书 `chat_id` 持久化：每个单聊、每个群聊分别保存，群内所有话题共用该群设置，主节点重启后继续生效。卡片只允许本次指令发起人操作；未配置的会话始终使用实时卡片回复。该设置只影响普通 AI 任务的卡片发送时机，不改变环境检测、绑定和菜单等专用消息。

### 环境检测快捷指令

发送 `环境检测` 可通过按钮选择 `feature`、`feature-tag（隔离环境）` 或 `uat/daily`；也可以直接发送 `环境检测 feature`、`tag 环境检测`、`UAT/Daily 环境检测` 等白名单别名。指令只忽略空格、大小写、全半角冒号和连接符后做精确匹配，普通问句不会被拦截。

快捷指令不创建 AI 会话、不加载聊天上下文或记忆，也不调用 AI Runner。普通 Worker 会在生效目录运行固定参数的 `nb info --output`，90 秒超时后把结构化结果交给主节点生成卡片。卡片异常优先展示，每页最多 8 个服务；黄色的“疑似被其他需求覆盖”会同时展示目标/实际分支和提交、构建人员作为证据。

单聊使用发送者所属普通 Worker 的当前生效目录。群聊只使用在线、类型为普通目录且声明 `custom_command.environment_check.v1` 能力的绑定；多个候选首次选择后按群记住该目录，后续检测不同环境也继续复用，目录失效、离线或能力消失时会要求重选。knowledge、VibOps、旧 Worker 和未安装 `nb` 的 Worker 不进入候选。

`knowledge` 引擎默认关闭单聊，仍可承接群聊知识库固定路由，但不会成为宿主的单聊候选。启动通知由主节点根据 Worker 上下线状态发送，展示运行引擎、当前模型、思考深度、从节点用户信息、服务范围和 `nikou-cli` 版本，并检查 npm 是否有更新；网络暂时不可用时只标记“无法检测更新”，不阻塞节点启动。同一 Worker 进程在 10 分钟内断线重连不会重复发送启动或离线通知；超过宽限期会发送离线提醒，恢复后发送恢复连接通知。

主节点会根据 Worker 上报的 `ops_user` 统一解析飞书宿主身份、职务和视角，并在连接握手中返回给 Worker。邮箱域统一配置在主节点的 `hook.owner_email_domain`（或主节点环境变量 `NIKOU_OWNER_EMAIL_DOMAIN`）。身份无法解析时，该 Worker 不会回退绑定白名单中的其他用户，也不会进入单聊路由；身份补齐并重新连接后才恢复单聊能力。飞书 App ID 和 App Secret 只保存在主节点；Worker 获取用户、群聊、消息历史、图片和附件时，通过已认证的主从通道调用受限飞书网关，不保存或接收飞书凭证。管理状态中的 `perspectiveStatus` 可区分 `identity_unresolved`（身份未补齐）和 `job_title_unmatched`（已获取职务但未命中视角）。

知识库 Worker 可以使用 `knowledge` 引擎直接连接 FastGPT 应用。此模式不会启动 Claude，也不会在 Worker 内重复连接知识库或代码 MCP；FastGPT 应用负责完成知识检索、内部工具调用和回答生成，Worker 只负责会话、流式事件与飞书卡片适配：

```json
{
  "knowledge": {
    "provider": "fastgpt",
    "base_url": "http://127.0.0.1:3000/api",
    "api_key_env": "NIKOU_FASTGPT_APP_KEY",
    "request_timeout_ms": 600000,
    "retain_dataset_cite": true
  }
}
```

API Key 必须来自 `api_key_env` 指定的环境变量，不支持把真实 Key 写入命令参数、README 或默认配置。`base_url`、`api_key_env`、`request_timeout_ms` 和 `retain_dataset_cite` 同时兼容 camelCase。

### 初始化配置

```bash
nikou-cli hook init
```

初始化只需要以上一条命令，不需要预先创建 JSON。命令按从节点模式初始化，只提示主节点地址和主从接入密钥，不再要求飞书 App ID 或 App Secret；所有 Secret 输入均不会回显。运行凭据写入 npm 包之外的本机私有目录，配置文件和备份权限固定为 `0600`，初始化摘要不会展示任何 Secret。默认主节点地址为 `127.0.0.1`；如果旧配置里已有非本机主节点地址，则沿用该地址。

内部批量安装可使用参数完成非交互初始化。推荐由安装系统注入 Secret 环境变量，避免密钥出现在 Shell 历史和进程参数中：

```bash
NIKOU_HOOK_SHARED_SECRET="$YOUR_HOOK_SHARED_SECRET" \
nikou-cli hook init \
  --master-ip 203.0.113.10 \
  --deployment slave \
  --yes
```

也兼容 `--shared-secret` 明文参数，但只建议在受控安装环境使用。主节点或本机一体模式仍可使用 `--app-id`、`--app-secret` 配置飞书凭证；从节点模式会忽略并移除本地飞书凭证。

单独从节点连接已有主节点时，不要求配置主节点专用的 `hook.bind_allowed_user_ids`。本机一体或单独启动主节点时，仍必须至少配置一个允许绑定的飞书 `user_id`。
从节点模式只写入 `~/.nikou-block/worker/config.json`，不再生成或覆盖主节点配置；
检测到历史 `~/.nikou-block/master/config.json` 时只告警，不会未经确认自动删除。

从节点生成的 Hook 配置保持最小化：

```json
{
  "hook": {
    "master_host": "203.0.113.10",
    "master_port": 19732,
    "shared_secret": "CHANGE_ME"
  }
}
```

AI 引擎、模型、本地目录以及 FastGPT 等引擎专用密钥仍属于本机运行配置，不会从主节点下发。升级时应先部署主节点，再逐个升级 Worker；新主节点兼容仍持有本地飞书凭证的旧 Worker，新 Worker 连接不支持飞书网关的旧主节点时会明确提示先升级主节点。

`ai-hook` 裸命令仅为个人节点兼容旧习惯，仍默认等价于 `nikou-cli hook worker codex`；如需在个人节点启动 Claude Worker：

```bash
ai-hook claude --model claude-opus-4-8
```

配置默认写入：

```text
~/.nikou-block/master/config.json
~/.nikou-block/worker/config.json
```

安全建议：

- 不要复制、提交或分享 `~/.nikou-block` 下的配置与备份；它们不会进入 npm 包。
- `shared_secret` 用于主从节点握手，生产环境不要复用机器人密钥。
- `knowledge_base_ip` 和 `knowledge_base_dir` 请按自己的机器和目录配置，默认值只是本机占位。
- API Key 优先通过 `api_key_env` 引用环境变量；也兼容只保存在本机配置中的 `api_key`，但禁止提交该配置。
- 从节点需要按系统用户名补齐飞书邮箱时，通过 `NIKOU_OWNER_EMAIL_DOMAIN` 配置邮箱域名；也会优先从已有身份缓存自动推断。公开示例：`export NIKOU_OWNER_EMAIL_DOMAIN=example.com`。

### 本机一体启动

默认 Codex：

```bash
ai-hook local
```

Claude：

```bash
ai-hook local claude --model claude-sonnet-4-6
```

Gemini：

```bash
ai-hook local gemini
```

API Agent：

```bash
ai-hook local api --api kiro
```

FastGPT 知识库客户端：

```bash
ai-hook local knowledge
```

完整入口也可以写成：

```bash
nikou-cli hook local codex
```

如果主节点监听 `0.0.0.0`，本机内嵌从节点会自动连接 `127.0.0.1`。

### 启动主节点

主节点首次启动前，必须在唯一授权主机上设置启动密码：

```bash
nikou-cli hook master-password --set
```

命令会隐藏读取并二次确认密码。JSON 配置只保存带盐哈希；密码本身只保存在当前
主机的 `~/.nikou-block/master/master-start-password`，文件权限为 `0600`。也可以通过
`NIKOU_MASTER_START_PASSWORD` 注入密码。未配置、缺少本机凭据或密码不匹配时，
`hook master` 和 `hook local` 都会在建立飞书长连接前拒绝启动。
同一系统用户下还会持有 `~/.nikou-block/master/master.lock` 独占锁，禁止用不同端口
重复启动第二个主节点；进程异常退出后会按锁内 PID 自动清理陈旧锁。

```bash
nikou-cli hook master
```

主节点完成启动并建立飞书长连接后，会向私有配置 `hook.master_notify_user_id` 指定的
固定飞书用户发送单聊启动卡片，不会从绑定白名单推断接收人。卡片包含主节点 IP、主节点
名称、当前 `nikou-cli` 版本、npm 最新版本和更新状态；如果能从本机
`~/ops/ops_global.properties` 读取到 `ops_user`，还会展示主节点名称。未配置固定接收人
时跳过通知。主节点、普通从节点和知识库固定从节点的启动通知统一使用卡片。

```json
{
  "hook": {
    "master_notify_user_id": "YOUR_FEISHU_USER_ID"
  }
}
```

覆盖监听参数：

```bash
nikou-cli hook master \
  --master-host 0.0.0.0 \
  --master-port 19732 \
  --shared-secret CHANGE_ME
```

怀疑存在未知旧主节点时，应在飞书后台轮换 App Secret，只把新 Secret 更新到授权主节点
和确实需要调用飞书 API 的受控 Worker，再重启并核验；禁止分发到其他机器。启动密码不能
替代 App Secret 轮换，因为它无法终止已经运行的旧版本进程。

### 主节点管理接口

主节点可以额外开一个 HTTP 管理接口，供 CC Switch 的「妮蔻 · 主节点」模块远程
查看运行状态、翻页读日志、看任务统计和触发重启。默认不启用，只有在主节点配置
`~/.nikou-block/master/config.json` 写入了 `hook.admin_key` 时才会监听。

生成管理密钥（在主节点机器上执行）：

```bash
nikou-cli hook admin-key --rotate
```

查看当前配置与密钥指纹：

```bash
nikou-cli hook admin-key
nikou-cli hook admin-key --show   # 打印完整密钥，注意不要外发
```

对应配置项：

| 配置 | 默认值 | 说明 |
| --- | --- | --- |
| `hook.admin_key` | 空 | 管理密钥，至少 24 位；为空时不启动管理接口 |
| `hook.admin_port` | `19733` | 管理接口监听端口 |
| `hook.admin_host` | `0.0.0.0` | 管理接口监听地址 |
| `hook.admin_enabled` | `true` | 配置了密钥后是否启用 |

接口清单（除 `/admin/ping` 外都需要 `X-Nikou-Admin-Key` 头，或 `Authorization: Bearer <key>`）：

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/admin/ping` | 连通性探测，只返回服务名与版本 |
| GET | `/admin/status` | 运行态：PID、启动时间、在线 Worker 摘要（含内网 IP、nikou-cli 版本、视角标签）、群聊绑定数、白名单人数 |
| GET | `/admin/log?page=1&pageSize=200&keyword=` | 主节点日志分页，`page=1` 为最新一页 |
| GET | `/admin/stats?days=7` | 按天任务统计，读本机 `usage-events.sqlite` |
| GET | `/admin/bind-whitelist` | 查询主节点当前绑定白名单 |
| POST | `/admin/bind-whitelist` | 按姓名、工号或 `user_id` 加入/移出绑定白名单 |
| POST | `/admin/restart` | 主节点自行退出，由 systemd `Restart=always` 拉起 |
| POST | `/admin/workers/:workerId/remote-support` | 指定 Worker 执行一次远程 AI 支持会话 |
| GET | `/admin/remote-support/threads?workerId=&page=1&pageSize=50` | 分页查看远程支持历史会话 |
| GET | `/admin/remote-support/threads/:threadId` | 查看一个会话的全部轮次、思考和工具事件 |
| GET | `/admin/remote-support/:requestId` | 查询远程支持会话及实时事件 |
| POST/DELETE | `/admin/remote-support/:requestId/cancel` | 取消远程支持会话 |
| POST | `/admin/ask` | 创建妮蔻抉择并发卡，立即返回 `ask_id` |
| GET | `/admin/ask/:askId?wait=30` | 长轮询抉择结果，最多挂起 30 秒 |
| POST/DELETE | `/admin/ask/:askId/cancel` | 取消抉择 |
| DELETE | `/admin/ask/:askId` | 取消抉择 |

安全边界：密钥只做常量时间比较，鉴权失败按来源 IP 限流（5 分钟内 8 次后返回
429），日志不打印密钥；返回体只含运行态与脱敏 Worker 摘要，不含飞书凭据、
`shared_secret` 或统计库账号。重启依赖 systemd 托管，未托管时接口直接拒绝。

#### 远程 Worker 支持会话

远程支持会话用于管理员从 CC Switch 直接联通、排查或复测指定从节点，不经过飞书
任务路由，也不会发送飞书消息或渲染飞书卡片。请求体示例：

```json
{
  "prompt": "test",
  "dirPath": "/workspace/your-project",
  "mode": "full_agent",
  "timeoutMs": 120000,
  "threadId": "OPTIONAL_EXISTING_THREAD_ID"
}
```

不传 `threadId` 时新建会话；传入已有 `threadId` 时，主节点会把该会话上一轮返回的
AI `sessionId` 交给同一 Worker 严格续聊。续聊固定原 Worker、引擎和执行目录；历史
会话不存在、引擎切换或底层会话已失效时会明确报错，不会静默新建导致上下文断开。
主节点将会话与每轮事件持久化到 `~/.nikou-block/state/remote-support-sessions.json`，
文件权限为 `0600`，主节点重启后仍可在 CC Switch 中查看和继续。

`full_agent` 会复用目标 Worker 当前启动的引擎、模型、思考深度、执行目录权限及
MCP/工具配置；从节点会实时回传状态、思考摘要、工具调用及结果、标准输出、错误
和使用量，管理员页面可直接查看这些事件。`raw_probe` 只用于基础联通探测，不启动
完整 Agent。执行目录必须是目标从节点本机存在且可访问的目录，路径不会在主节点上
执行。新协议通过 Worker 启动握手中的可选能力字段协商，旧 Worker 不支持时会被
主节点明确拒绝，不影响原有飞书任务。

### 启动从节点

Codex：

```bash
nikou-cli hook worker codex
```

Claude：

```bash
nikou-cli hook worker claude
```

Gemini：

```bash
nikou-cli hook worker gemini
```

Kiro 原生（直接使用本机 Kiro CLI 与登录态，不经过 API 反代）：

```bash
nikou-cli hook worker kiro
```

未传 `--model` / `--effort` 时才继承本机 Kiro 默认配置；由 CC Switch 或飞书菜单拉起时会显式固定。也可以自己指定模型、Agent、思考强度和 Agent Engine：

```bash
nikou-cli hook worker kiro \
  --model claude-opus-4.8 \
  --agent YOUR_AGENT \
  --effort high \
  --agent-engine v2
```

Kiro Worker 通过 ACP 管理会话并默认信任全部工具，支持正文、思考摘要、工具状态、图片和使用量流式更新。飞书图片消息、富文本内嵌图片以及被引用消息中的图片会在任务执行前下载到 Worker 临时目录，并作为图片内容传给 Kiro；提示词会给出统一资源目录，执行结束后自动清理。Kiro 不返回精确 input/cache/output Token，卡片改为展示当前实际模型、上下文占用、Credits、耗时和 effort；即使未传 `--model`，也会读取 ACP 会话返回的当前模型。思考内容仅在所选模型实际发送 `agent_thought_chunk` 时出现。

飞书普通文件附件（例如 Excel、PDF）及被引用消息中的附件会由从节点下载到当前任务目录下的统一临时资源目录，提示词会提供该目录供 AI 用本机工具读取。被引用的互动卡片会提取可读正文并加入引用上下文；卡片或富文本中能识别出的图片、附件也会按同样方式处理。实际能否解析具体格式，取决于从节点是否安装了对应的解析工具或运行库。任务结束后资源目录会自动清理。

所有引擎的会话 ID、模型、用量和耗时统一放在“思考过程”顶部，不再单独占用卡片正文区域。摘要按引擎能力展示，拿不到的指标整行不展示，不输出“未提供精确 Token”这类占位文案：

| 行 | 内容 | 说明 |
| --- | --- | --- |
| 会话 ID | `8f2c…（续聊第 3 轮）` | 第 1 轮显示`（新会话）`，轮次按从节点本机会话记录累加，可用于排查上下文是否断开 |
| 模型 | `(kiro)claude-opus-5 · xhigh` | 统一带引擎前缀，后面是实际生效的思考深度 |
| 用量 | Kiro：`上下文 26.9% · Credits 91.312`；Codex/Claude 等：`input 12.3k · cache 8.0k · output 1.2k（缓存命中 65%）` | 只展示该引擎真实提供的指标；缓存命中率按各自 Token 口径计算 |
| 执行 | `工具 12 次 · 成本 $0.42` | 工具次数由从节点按调用 ID 去重统计，全引擎可用；成本仅 Claude 等会返回该字段的引擎展示 |
| 耗时 | `27min（模型 24min）` | 主耗时为主节点统计的“收到事件 → 完成”，括号内是引擎上报的本轮模型耗时；不足 1 分钟用秒，超过 1 小时展示 `1h6min` |

思考正文只保留最新 20 行且最多 4000 字符；不超过 5 行时直接展开，从第 6 行开始自动折叠，后续流式更新不会反复重置用户的展开状态。折叠后标题会变成「🔍 思考过程（点击展开）」，避免同事以为面板里没有内容。

实时卡片在运行期间保持可见的动效，避免长任务被误认为卡死：

| 位置 | 运行中 | 结束后 |
| --- | --- | --- |
| 卡片顶部运行提示 | `🕐 运行中 已用 1min ...`，时钟图标与点数每秒推进一帧，点数按 `.`→`..`→`...`→`....`→`...`→`..` 往复 | 提示清空 |
| 思考过程（尚无思考内容） | `🕐 妮蔻正在思考 ...` 动画占位 | 换成实际思考正文或「执行完成」 |
| 执行结果（尚无输出） | `🕐 等待输出 ...` 动画占位 | 换成实际结果 |

动画每秒只推进一帧，已用时长按帧取快照，因此高频流式刷新不会额外增加卡片更新请求；有正文的区域不再渲染占位动画，只保留顶部运行提示。任务完成、失败、取消或最长 2 小时后动画自动停止。该能力只涉及主节点卡片渲染，主从消息协议保持不变；最终卡片模式（`最终卡片回复`）不发送进度卡片，因此不受影响。

Kiro / Codex 任务连续 60 秒没有新进展时，会在卡片中定期提示等待时长和“尚未收到完成信号”；这不代表任务已成功，也不会自动重跑任务。Kiro 工具状态支持增量更新，达到输出上限、拒绝或取消等非正常结束会显示错误。Codex 收到本轮完成事件后最多再等待 2 秒退出清理，避免进程残留让卡片一直停在执行中；收到本轮失败事件则立即报告失败。

卡片更新请求有 10 秒等待上限，最终刷新最多尝试 3 次；实时卡片创建或发送失败时，会在原会话发送一张完整结果卡片；已有实时卡片的最终更新仍失败时，改用普通文本告知最终状态，避免重复发送同款卡片。任务完成会等待正在创建的卡片，避免快任务留下“等待输出”的空卡片。此改进只涉及主节点卡片渲染，保持现有主从消息协议兼容。

API Agent：

```bash
nikou-cli hook worker api --api kiro
```

FastGPT 知识库客户端：

```bash
nikou-cli hook worker knowledge
```

### 外部 Agent Worker

`vibops` 是一个公开的外部 Agent 桥接入口：CLI 只通过本机已安装的
`nikou-agent-bridge --provider vibops --stdio` 交换通用 NDJSON 事件，不包含供应商地址、凭据或私有配置。
桥接程序由私有扩展包提供，且必须与本机 Worker 一起安装。

```bash
nikou-cli hook worker vibops
```

该 Worker 使用独立的 `worker-vibops.json` 状态文件，不启动浏览器控制服务，也不占用本地浏览器控制端口。群聊外部 Agent 绑定只保存 Agent ID、显示名称、模型和群关系；同一群可以有多个外部 Agent，并按飞书话题复用会话。

在已配置绑定白名单的群聊中，发送 `@机器人 绑定 Agent`、`@机器人 绑定Agent` 或 `@机器人 绑定vibops`，机器人会从唯一在线的 `vibops` Worker 获取可用 Agent 并发送选择卡片。选择后即可在群中 `@机器人` 提问；多个在线 VibOps Worker 时会拒绝绑定，避免随机选错宿主。

`knowledge` 模式调用 FastGPT `/api/v1/chat/completions`，固定开启流式响应、详细事件和知识库引用。飞书话题会映射为稳定的 FastGPT `chatId`，因此同一话题可以连续追问，Worker 重启后也会恢复映射。FastGPT 返回的正文进入卡片“执行结果”，可见推理摘要进入“思考过程”，工作流节点和工具调用以脱敏状态行展示；不会展示工具参数和完整工具返回值。

首版仅支持文本问答。图片消息和 FastGPT 交互节点会返回明确的不支持提示，不会静默忽略。

连接远端主节点：

```bash
nikou-cli hook worker codex \
  --master-host 203.0.113.10 \
  --master-port 19732 \
  --shared-secret CHANGE_ME
```

Claude 参数透传示例：

```bash
nikou-cli hook worker claude \
  --model claude-sonnet-4-6 \
  --permission-mode acceptEdits \
  --max-budget-usd 5
```

使用 `ai-hook` 入口时，默认启动 Codex Worker；第一个参数可指定引擎：

```bash
ai-hook
ai-hook claude --model claude-sonnet-4-6
ai-hook gemini
ai-hook kiro
ai-hook api --api kiro
```

知识库宿主机不使用 `ai-hook` 兼容入口，必须使用唯一的 `nikou-cli hook worker knowledge` 启动方式。

从节点连接主节点成功后，会向宿主发送启动卡片。卡片包含当前运行引擎、实际启动模型、思考深度、从节点用户姓名/工号/邮箱/VOPS 用户/飞书身份和单聊/群聊可用范围；通过飞书菜单切换模型并重启后，新卡片同时作为切换成功确认。

### 飞书机器人菜单事件

主节点支持以下菜单事件：

| 事件 | 行为 |
| --- | --- |
| `chat_new` | 标记下一条单聊消息强制创建新会话，不复用旧消息引用或最近会话 |
| `switch_path` | 发送单聊目录切换卡片，可浏览、收藏、选择目录或切回默认目录 |
| `switch_session` | 展示当前目录下最近单聊会话；点击会话后，下一条消息继续该会话 |
| `recent_sessions` | 展示跨目录最近单聊会话；点击后同时切换目录和会话，下一条消息继续该会话 |
| `stop_chat` | 终止当前宿主 Worker 正在执行的全部单聊和绑定群聊任务 |
| `switch_account` | 展示当前引擎支持的 CC Switch 账号卡片；Claude / Codex / Kiro 都可选择当前引擎下的账号，卡片会附带用量与规则摘要，切换后按当前运行档案重启 Worker，并同步刷新运行中的 CC Switch 当前账号和代理目标 |
| `switch_model` | 按「① 会话类型（单聊 / 群聊）→ ② 引擎 → ③ 模型 → ④ 思考深度」选择。同引擎只改模型或思考深度时点击“保存到单聊/群聊并立即生效”，不重启 Worker；换引擎时按钮变为“切换并重启”。档位候选按所选模型解析，换模型后若原档位不可用会自动落到该模型的默认档位 |
| `bind_chat` | 发送群聊名、目录简写和解绑操作组成的交互卡片 |

`my_info` 已不再处理。`bind_chat` 卡片以主节点持久绑定表为准，并按页展示绑定，避免大量绑定超过飞书卡片元素上限；点击某行“解绑”后会持久撤销对应绑定并刷新当前页，旧 Worker 快照不能恢复已解绑记录。

`switch_path` 以 Worker 本机 `~/ops/ops_global.properties` 的 `code_path` 作为默认目录，但不限制浏览根目录。用户可以通过“上一级”一直导航到文件系统根目录，并选择 Worker 账号有权限访问的任意文件夹；可访问的目录软链接也允许进入。选择结果按飞书用户持久保存；卡片会同时展示当前目录、默认目录和正在浏览的目录，并支持上一级、进入子目录、分页、切换到当前浏览目录和切回默认。目录发生切换后会开启新的单聊会话，切换前的最近会话和旧消息引用都不会继续复用。点击“进入”、翻页、切换或切回默认时，卡片会在同一条消息上直接刷新；若从节点返回较慢，主节点会在结果到达后异步刷新这条卡片，卡片按钮始终保持可点击。

`switch_session` 和 `recent_sessions` 查询当前 Worker 通过妮蔻产生的单聊会话，以及当前运行模型由本机 CLI 发起的 Kiro 会话；按当前引擎过滤，不限制会话更新时间，统一按更新时间倒序只展示最新 8 条。模型只用于卡片展示，不参与会话筛选。卡片表格展示会话标题、来源、目录、模型和更新时间，会话标题优先使用最近一次真实用户问题，没有标题的历史会话使用会话 ID 简称。点击当前目录卡片中的会话只切换会话；点击跨目录卡片中的会话会同时切换目录和会话。选择本地 CLI 会话后，下一条单聊消息会由当前 Worker 加载该 Kiro session 继续执行；本地 CLI 会话正文仍保存在 Kiro 本地会话目录，Worker 只保存当前用户的外部会话引用。两种卡片都会在原消息上更新，超时或更新失败时按现有异步刷新和重新发送机制处理；重复点击“当前会话”保持幂等。

卡片还支持目录收藏，每个飞书用户最多收藏 5 个目录：点击“收藏此目录”把正在浏览的目录加入收藏，收藏满 5 个后该按钮置灰，需要先在对应目录点击“取消收藏”。收藏区固定在卡片顶部，按钮只展示目录的最后一级名称（超长会截断），点击即直接切换到该目录，当前正在使用的收藏会高亮。收藏按飞书用户持久保存在主机状态文件中，只是快捷入口，不会额外提升会话代次；收藏目录被删除或变为不可访问时，下次打开卡片会自动清理。

单聊续聊由底层会话保存上下文，Worker 只传递当前问题，不再重复读取或注入“上次 AI 回复后的人类消息”。会话来源（单聊/群聊、发起人、群名）独立保存在 Worker 状态中，供本机只读会话列表展示，不依赖提示词解析。

任务结果卡片中的“停止”按钮允许所有成员操作，不校验点击人身份。取消消息会按 `task_id` 定向发送给实际执行该任务的 Worker，单个任务的取消或结果处理异常不会影响主节点和其他 Worker。

也可以通过 `--` 传递底层工具参数：

```bash
nikou-cli hook worker claude -- --model claude-sonnet-4-6
```

### Kiro 原生与 API Kiro

- `ai-hook kiro` / `nikou-cli hook worker kiro`：启动本机 Kiro CLI 的 ACP Agent，使用 `~/.kiro` 中的本机登录态、MCP、Skills 和设置。
- `ai-hook api --api kiro`：直接调用 `~/.nikou-block/worker/config.json` 中名为 `kiro` 的 API 反代 profile，两者会话与认证互不混用。

Kiro 原生会按目录和飞书话题保存 session ID；Worker 重启或模型切换后会继续加载同一会话。Hook 不暴露 Kiro 终端中的 `/rewind`、`/compact`、会话列表和删除界面。

群聊首条消息转为话题后，所有引擎共用的会话解析会核实根消息的群和话题归属，将消息 ID 与话题 ID 关联到原会话。任务失败、超时或 Worker 重启后，后续追问仍复用已保存的会话；旧版本已产生不同映射时优先恢复核实过的原消息会话。引用其他话题不会合并会话，归属查询失败时暂停处理并提示重试，避免静默新建会话。

主节点的 Agent 选择也使用相同的话题归属校验。Kiro 每轮 ACP 子进程会注入当前任务的抉择上下文；Codex 收到会话 ID 后立即保存映射，即使该轮失败仍可继续原会话。

Job 接口兼容旧客户端：`GET /api/events` 携带 `Accept: text/event-stream` 时保留实时订阅；新客户端可使用 `/api/events/stream`，事件列表仍由 `/api/events` 返回 JSON。旧客户端修改顶层 `schedule` 时，该值优先于回传的旧 `triggers` 排期。

群聊任务会按提问者的飞书职务名称加载角色提示词，当前支持开发、产品、测试、技术支持和运营。角色提示词保存在 `~/ops/nikou-prompts/roles/`；未匹配职务时不注入角色约束。技术支持角色会优先核实线上日志、接口、数据和运行状态，再用业务可理解的语言说明是否存在问题，默认不展开源码位置。运营角色会优先说明用户影响、业务范围和可执行的处理建议，线上问题会先核实现象、日志、接口、数据和运行状态。

### API 模式

API 模式不依赖 Codex、Claude 或 Gemini CLI，直接调用模型反代，并在本地完成会话、MCP 和 Skills 工具循环。现支持：

- `anthropic`：Anthropic Messages API，适用于 KiroProxy 等 Claude Code 兼容反代。
- `openai-responses`：OpenAI Responses API，适用于支持 `/v1/responses` 的 NewAPI/Codex 类反代。
- `openai-chat`：OpenAI Chat Completions API，适用于只支持 `/v1/chat/completions` 的兼容反代。

三种协议均使用流式响应：最终回答会增量更新到卡片的“执行结果”，Provider 显式返回的 `thinking`、`reasoning_content` 或 reasoning summary 会增量更新到“思考过程”。这里展示的是面向用户的思路摘要，不是模型隐藏的完整思维链。工具调用参数会在流中完整组装后再执行。若反代忽略流式参数并返回普通 JSON，API 模式会自动回退为整段响应，不影响最终结果。

通过 `--api <profile>` 切换配置，不影响现有 `codex`、`claude`、`gemini` 模式：

```bash
ai-hook api --api kiro --model claude-opus-4.5
ai-hook api --api kiro --model claude-sonnet-4.5
ai-hook api --api newapi
```

也可以临时覆盖非敏感参数：

```bash
ai-hook api \
  --api newapi \
  --api-type openai-chat \
  --base-url https://api.example.com/v1 \
  --api-key-env NEWAPI_API_KEY \
  --model YOUR_MODEL \
  --mcp-config ~/.nikou-block/api/mcp.toml \
  --skills-dir ~/.nikou-block/api/skills
```

profile 可以用 `models` 配置多个模型，列表第一个是默认模型，启动时通过 `--model <model-id>` 切换。原有单值 `model` 仍然兼容。

同一个反代同时提供多种协议时，可用 `model_types` 为指定模型覆盖 profile 的默认协议。例如 KiroProxy 的 Claude 模型走 `anthropic`，GPT-5.6 Sol/Terra/Luna 走 `openai-responses`，但仍共用同一个 profile、Base URL、Key 和 SQLite 会话。

Anthropic 兼容反代需要显式请求思考块时，在 profile 中配置 `thinking_enabled: true`；`thinking_budget_tokens` 控制思考预算，默认 4000。代理返回的标准 `thinking_delta` 会实时渲染到卡片“思考过程”。同一 profile 切换到 OpenAI Responses 模型时，该开关会请求 `reasoning.effort=high` 和 `reasoning.summary=detailed`，兼容代理应返回 `response.reasoning_summary_text.delta`。

API 模式会把会话写入 `api.session_db_path` 指定的 SQLite 数据库。数据库包含 thread、binding、turn、item 四层记录；群话题和单聊续聊继续沿用现有 Hook 的 `session_id` 协议。所有 API profile 和模型共用同一会话命名空间，切换 `--model` 后会继续当前群聊/话题对应的上下文。完整历史保留在数据库中，发送给模型的上下文受 `max_history_chars` 限制，单次工具结果受 `max_tool_output_chars` 限制（默认 40000 字符）。

API Skills 与 Codex、Claude 和 `.agents/skills` 完全隔离，默认只扫描 `~/.nikou-block/api/skills`。目录下每个 Skill 使用 `<name>/SKILL.md` 结构；模型先看到全部 Skill 名称和预算内的描述，命中后通过 `skill_read` 分段读完 `SKILL.md`。Skill 引用的说明、模板和参考文件通过 `skill_read_resource` 继续读取，目录内 Python、Shell、Node 或可执行脚本通过 `skill_run_script` 直接运行。三类工具均由本地 Worker 自动执行，不弹出授权确认。

需要复用现有 Codex 配置时，执行一次独立导入：

```bash
nikou-cli hook api-import-codex
```

该命令只复制 Codex `mcp_servers` 和 `~/.codex/skills`、`~/.agents/skills` 下的 Skills，不创建软链，也不会回写 Codex 配置；同名项默认跳过，需覆盖时增加 `--force`。目标路径仍可通过 `--mcp-config` 和 `--skills-dir` 指定：

```bash
nikou-cli hook api-import-codex \
  --mcp-config ~/.nikou-block/api/mcp.toml \
  --skills-dir ~/.nikou-block/api/skills \
  --force
```

运行 Worker 时仍可用 `--skills-dir <path>` 临时指定其他目录，或在配置中指定：

```json
{
  "skills": {
    "enabled": true,
    "directory": "~/.nikou-block/api/skills",
    "max_metadata_chars": 8000,
    "max_skill_chars": 40000,
    "max_resource_chars": 40000,
    "script_timeout_ms": 120000
  }
}
```

API MCP 默认开启，使用独立的 `~/.nikou-block/api/mcp.toml`，不读取也不修改 Codex 配置。首次启动时如果默认文件不存在会自动创建空模板。支持 Codex `mcp_servers` TOML 形式的 stdio 和 Streamable HTTP、`tools/list` 分页、`enabled_tools`/`disabled_tools` 过滤和 `required` 启动约束；`servers` 为空表示加载全部启用的 server，也可只启用指定 server：

```json
{
  "mcp": {
    "enabled": true,
    "config_path": "~/.nikou-block/api/mcp.toml",
    "servers": ["example"],
    "startup_timeout_ms": 15000,
    "tool_timeout_ms": 120000
  }
}
```

独立 MCP TOML 示例：

```toml
[mcp_servers.local]
command = "npx"
args = ["-y", "YOUR_MCP_PACKAGE"]
enabled = true

[mcp_servers.remote]
url = "https://mcp.example.com/mcp"
bearer_token_env_var = "REMOTE_MCP_TOKEN"

[mcp_servers.remote.http_headers]
X-Client = "nikou-api"

[mcp_servers.remote.env_http_headers]
X-Workspace-Token = "WORKSPACE_TOKEN"
```

可通过 `--mcp-config <path>` 临时指定其他 TOML 文件。API MCP 默认开启，需要临时关闭时使用 `--no-mcp`。

API Worker 默认把全部 MCP 与 Skill 工具定义发给模型，不设置数量上限。若某个第三方反代自身限制工具数量，可在对应 profile 中设置 `max_model_tools`；`0` 或不配置表示不限制。超过展示上限时，模型仍可通过内置 `api_tool_search` 和 `api_tool_call` 搜索并调用完整本地工具集。

API 模式依赖 Node.js 内置 `node:sqlite`，要求 Node.js 22.5 或更高版本；其他三个模式继续兼容 `package.json` 声明的 Node.js 18+。可用 `--no-mcp` 或 `--no-skills` 临时关闭对应能力。

### 查看日志

```bash
nikou-cli hook log
nikou-cli hook log --master
nikou-cli hook log --worker
nikou-cli hook log "trace-id-or-keyword" --context 30
nikou-cli hook log --follow
```

日志默认位于：

```text
~/.nikou-block/logs/master/ai-hook-master.service.log
~/.nikou-block/logs/worker/ai-hook-worker.service.log
```

## 统计上报

统计事件默认由主节点写入本地 SQLite。旧的 MySQL 日汇总为可选兼容能力，只允许通过主节点本机私有配置 `~/.nikou-block/master/config.json` 开启；连接信息不会从环境变量读取，也不会写入公开仓库或日志：

```json
{
  "usage_stat": {
    "enabled": true,
    "host": "YOUR_DB_HOST",
    "port": 3306,
    "user": "YOUR_DB_USER",
    "password": "CHANGE_ME",
    "database": "YOUR_DB_NAME",
    "table": "nikou_usage_daily_stat",
    "event_db_path": "~/.nikou-block/master/usage-events.sqlite"
  }
}
```

配置文件必须保持 `0600` 权限。只读汇总使用固定命令，不支持传入任意 SQL：

```bash
nikou-cli hook usage-report --start 2026-07-13 --end 2026-07-19
```

主节点会把任务生命周期事件异步写入本机 SQLite 文件，默认路径为 `~/.nikou-block/master/usage-events.sqlite`，首次启动自动建表并通过 `event_id` 幂等。事件只包含统计维度，不包含问题正文、回答正文或思考过程；不需要额外建表、数据库账号或远程网络。旧的 MySQL 日汇总仍作为兼容字段读取，但不再是周报事件统计的前置条件。详见 [统计事件存储说明](docs/usage-stat-event-schema.md)。

### Kiro 会话管理

`nikou-cli` 会从 `KIRO_HOME/sessions/cli` 读取 Kiro 官方会话文件，并与妮蔻绑定、Worker 实时阶段和锁状态合并。只读输出不会包含隐藏思考、系统前置或工具敏感参数。
妮蔻会话的 JSON 输出同时包含 `chatType`、`chatTypeLabel`、`chatName`、`chatId` 和 `requesterName`，这些字段优先读取 Worker 独立保存的会话来源元数据，并兼容解析旧会话提示词。

```bash
nikou-cli hook kiro-session list --format json
nikou-cli hook kiro-session list --limit 120 --format json
nikou-cli hook kiro-session show <session-id> --format json
nikou-cli hook kiro-session cancel <session-id> --yes
nikou-cli hook kiro-session detach <session-id> --yes
nikou-cli hook kiro-session delete <session-id> --yes
```

- `list` 输出里 `count` 是磁盘上的会话总数，`returned` 是本次返回条数，`statusCounts` 和 `nikouCount` 始终按全量统计。
- `--limit <n>` 只按最后事件时间倒序裁剪返回的列表长度，不影响上面这些统计字段：看板类调用方只展示最近若干条，但计数必须准。
- `list` 不会整解事件日志：末条事件类型只读行首，标题从尾部按行反查到第一条可见提问，群聊来源先用字节查找妮蔻提问前缀标记再解析命中那一行。会话数上千、单个日志上百 MB 时这一点决定了命令是秒级还是十秒级。
- `cancel` 只允许终止当前妮蔻 Worker 正在执行的 Kiro 会话。
- `detach` 会清除该 session 的直接引用和所有话题引用；活动会话会拒绝解除。
- `delete` 会拒绝活动或存在锁的会话，并通过 Kiro 官方删除命令执行。
- Worker 控制面只监听权限为 `0600` 的本机 Unix Socket；`kiro-runtime.json` 同样为 `0600`，且不记录提示词、回答或飞书 ID。

## 开发

```bash
npm install
npm run build
npm test
npm run dist:check
npm run privacy-scan
npm run pack:check
```

本地调试：

```bash
npm link
nikou-cli --help
```

## 发布

发布前必须通过：

```bash
npm run build
npm test
npm run dist:check
npm run privacy-scan
npm run pack:check
```

发布到公共 npm：

```bash
npm publish --registry=https://registry.npmjs.org/
```

## 许可证

从 `0.1.10` 起，本项目使用专有软件许可证，不是开源软件。仅允许安装和运行官方发布的未修改版本；
未经书面许可，不得修改、逆向工程、再分发、转售或制作衍生作品。完整条款见 `LICENSE`。

发布到 npm 的 JavaScript 会在构建后自动混淆，并通过 `npm run dist:check` 检查。
混淆只能提高分析和逆向成本，不能替代许可证约束，也不能作为密钥保护手段。

### Kimi Code CLI

支持新版 Kimi Code CLI（基于 0.43.1 的 ACP 能力验证），不使用旧 Python `kimi-cli` 配置。
先在原生 CLI 完成 `kimi login` 或供应商/API 配置。订阅账号与 API 的凭据、额度和默认模型都由 Kimi 管理；妮蔻不复制凭据、不接入 CC Switch，也不提供虚构的账号额度。

```bash
nikou-cli hook worker kimi
nikou-cli hook local kimi
ai-hook kimi --model YOUR_PROVIDER/YOUR_MODEL
nikou-cli hook worker kimi --model YOUR_PROVIDER/YOUR_MODEL --effort high --kimi-cli-bin /opt/kimi/bin/kimi
```

不指定 `--model` 时使用 Kimi 原生默认模型；原生供应商已配置但默认模型仍指向订阅账号时，需在 Kimi 中切换默认模型，或用 `--model` 显式指定供应商模型别名。思考选项从所选模型的 ACP 能力读取，不支持的档位明确报错，可用 `hook worker-options --engine kimi --model <别名>` 先查清楚。CLI 路径可通过 `--kimi-cli-bin`、`KIMI_CLI_BIN` 或 Hook 配置指定：

```json
{
  "hook": {
    "kimi_options": {
      "command": "/opt/kimi/bin/kimi",
      "model": "YOUR_PROVIDER/YOUR_MODEL",
      "effort": "high"
    }
  }
}
```

兼容 `kimiOptions`、`cli_bin`/`cliBin` 及 `kimi_cli_bin`/`kimiCliBin`。Kimi 子进程继承 `KIMI_CODE_HOME`。每轮新建或恢复会话后确认 ACP `auto` 模式已生效；若当前版本仍要求审批，会明确失败，避免飞书任务挂起。

群聊、话题和单聊复用现有飞书执行与消息兜底链路，支持流式文本、工具进度、图片和停止。Kimi 的会话引用与 Kiro/Codex 隔离；恢复历史不会重复输出旧回答。模型菜单使用原生 ACP 模型别名切换供应商，登录及登出仍在原生 CLI 完成。知识库宿主机不新增 Kimi Worker。

```bash
nikou-cli hook kimi-session list --limit 20
nikou-cli hook kimi-session show SESSION_ID
nikou-cli hook kimi-session continue SESSION_ID
nikou-cli hook kimi-session cancel SESSION_ID --yes
nikou-cli hook kimi-session detach SESSION_ID --yes
nikou-cli hook kimi-session delete SESSION_ID --yes
```

列表、详情、恢复和删除通过原生 ACP 获取；`continue` 打开原生交互会话。取消仅作用于当前妮蔻 Worker 的活动会话，删除/解绑要求 `--yes`，删除后清理妮蔻引用。飞书会话菜单也可选择本机 Kimi 历史会话继续。执行快照保存在妮蔻状态目录的 `kimi-runtime.json`，独立于 Kiro。

```bash
nikou-cli add OWNER/REPOSITORY --tool kimi
nikou-cli list --tool kimi
nikou-cli remove SKILL_NAME --tool kimi
nikou-cli init SKILL_NAME --tool kimi
nikou-cli mcp add MCP_SLUG --tools kimi
nikou-cli mcp list --tools kimi
nikou-cli mcp remove SERVER_NAME --tools kimi
nikou-cli ask-mcp install --tools kimi
```

Kimi Skills 写入 `$KIMI_CODE_HOME/skills`，MCP 写入 `$KIMI_CODE_HOME/mcp.json`；未设置时使用 `~/.kimi-code`。写入保留已有 MCP 条目；`--tool kimi` 不能与自定义 `--dest` 同时使用。

Kimi 执行卡片沿用统一思考面板摘要，显示 `(kimi)模型别名`、ACP 实际思考档位、上下文占用、工具次数、会话轮次和耗时。上下文占用不折算为本轮输入/输出 Token；CLI 未提供的 Token、费用和额度不展示。
