创建时间: 2026-02-10 16:17 GMT+8 版本: v1.0 状态: 待开发
实现 OpenCode TUI 与 OpenClaw 之间的双向通信:
- OpenClaw → OpenCode:接收用户需求并转发
- OpenCode → OpenClaw:通过 Hook + MCP 实时通知任务状态
| 角色 | 说明 |
|---|---|
| 用户 | Napsta6100k,使用 Telegram 发送需求 |
| OpenClaw | 消息网关,转发消息到 TG 群组 |
| OpenCode TUI | AI 编程工具,运行在用户 Mac 上 |
Telegram 用户
│
▼
OpenClaw Gateway (接收消息)
│
▼ (ACP 协议 / HTTP)
OpenCode TUI (执行任务)
│
▼ (Hook + MCP)
OpenClaw Gateway (接收通知)
│
▼
Telegram 群组 (转发结果/确认请求)
| 组件 | 文件位置 | 职责 |
|---|---|---|
| kiro-hook.js | ~/.config/opencode/plugins/ |
OpenCode 插件,监听 events,调用 MCP |
| kiro-mcp-server | ~/.config/opencode/plugins/ 或独立 |
MCP Server,接收工具调用,发送 HTTP 通知 |
| OpenClaw Gateway | /Users/openclaw/.openclaw/ |
接收 HTTP 通知,转发 TG |
┌─────────────────────────────────────────────────────────────┐
│ OpenCode TUI │
│ │
│ User Input (TUI) │
│ │ │
│ ▼ │
│ Sisyphus Agent 执行任务 │
│ │ │
│ ▼ │
│ Hook System 监听 events │
│ │ │
│ ├─▶ session.idle ──▶ 发送 phase_complete │
│ ├─▶ permission.asked ──▶ 发送 confirm_request │
│ ├─▶ permission.replied ──▶ 发送 confirm_reply │
│ └─▶ session.error ──▶ 发送 error │
│ │
│ │ │
│ ▼ │
│ Custom MCP Tool (notify_openclaw) │
│ │ │
│ ▼ │
│ HTTP POST → OpenClaw Gateway │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway │
│ │
│ HTTP Server (接收 MCP 通知) │
│ │ │
│ ▼ │
│ Message Parser (解析 format 类型) │
│ │ │
│ ├─▶ confirm_request ──▶ 格式化 → TG │
│ ├─▶ phase_complete ──▶ 格式化 → TG │
│ ├─▶ confirm_reply ──▶ 格式化 → TG │
│ └─▶ error ──▶ 格式化 → TG │
│ │
└─────────────────────────────────────────────────────────────┘
│
▼
Telegram 群组 (-5088311083)
触发条件: Agent 需要执行敏感命令,等待用户批准
MCP Tool 调用参数:
{
"toolName": "notify_openclaw",
"args": {
"format": "confirm_request",
"action": "RUN",
"command": "rm -rf node_modules",
"reason": "Deleting node_modules to clean dependencies",
"risk_level": "high",
"timestamp": "2026-02-10T16:00:00Z"
}
}TG 转发格式:
🔴 RUN: [command]
原因: [reason]
风险: [risk_level]
请回复「RUN」或「REJECT」
示例:
🔴 RUN: rm -rf node_modules
原因: Deleting node_modules to clean dependencies
风险: high
请回复「RUN」或「REJECT」
触发条件: Agent 完成一个阶段的任务,需要汇报
MCP Tool 调用参数:
{
"toolName": "notify_openclaw",
"args": {
"format": "phase_complete",
"session_id": "ses_abc123",
"content": "已完成以下任务:\n1. ✅ 初始化项目结构\n2. ✅ 安装依赖",
"stopReason": "end_turn",
"next_step_hint": "请输入下一阶段的目标"
}
}TG 转发格式:
📋 PHASE_COMPLETE: [summary]
---
[content]
[next_step_hint]
示例:
📋 PHASE_COMPLETE: 第一阶段完成
---
已完成以下任务:
1. ✅ 初始化项目结构
2. ✅ 安装依赖
3. 🔧 修复了 TypeScript 配置错误
请输入下一阶段的目标
触发条件: 用户回复 RUN 或 REJECT 后通知 Agent
MCP Tool 调用参数:
{
"toolName": "notify_openclaw",
"args": {
"format": "confirm_reply",
"action": "RUN",
"original_command": "rm -rf node_modules",
"user_id": "1018889184"
}
}TG 转发格式:
✅ 你已批准执行:rm -rf node_modules
继续执行中...
触发条件: 会话发生错误
MCP Tool 调用参数:
{
"toolName": "notify_openclaw",
"args": {
"format": "error",
"error_type": "permission_denied",
"message": "无法读取文件,权限被拒绝",
"session_id": "ses_abc123"
}
}TG 转发格式:
❌ ERROR: [message]
类型: [error_type]
~/.config/opencode/plugins/
├── kiro-hook.js # 主插件文件
├── kiro-mcp-server.js # MCP Server
└── package.json # 依赖配置
// kiro-hook.js
export const KiroHookPlugin = async ({ client, $, project, directory }) => {
// MCP Server URL,从环境变量读取
const MCP_SERVER_URL = process.env.KIRO_MCP_SERVER_URL || 'http://localhost:3000';
return {
/**
* Event Hook - 监听所有事件
*/
'event': async ({ event }) => {
// session.idle - 阶段完成
if (event.type === 'session.idle') {
await notifyOpenClaw('phase_complete', {
session_id: event.sessionId,
content: event.message,
stopReason: event.stopReason
});
}
// permission.asked - 需要确认
if (event.type === 'permission.asked') {
await notifyOpenClaw('confirm_request', {
action: event.permissionType, // 'run' or 'reject'
command: event.command,
reason: event.reason,
risk_level: assessRisk(event.command)
});
}
// session.error - 错误
if (event.type === 'session.error') {
await notifyOpenClaw('error', {
error_type: event.errorType,
message: event.message
});
}
}
};
/**
* 发送通知到 OpenClaw
*/
async function notifyOpenClaw(format, data) {
const payload = JSON.stringify({
format,
timestamp: new Date().toISOString(),
...data
});
await $`curl -X POST ${MCP_SERVER_URL}/notify \
-H "Content-Type: application/json" \
-d ${payload}`;
}
/**
* 评估命令风险等级
*/
function assessRisk(command) {
const highRiskPatterns = [
/^rm\s+-rf/,
/^dd\s+/,
/^mkfs/,
/^chmod\s+-R\s+777/,
/^chown\s+-R/
];
for (const pattern of highRiskPatterns) {
if (pattern.test(command)) return 'high';
}
return 'medium';
}
};// kiro-mcp-server.js
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
// 创建 MCP Server
const server = new McpServer({
name: 'kiro-notify',
version: '1.0.0'
});
// 注册 notify_openclaw 工具
server.tool(
'notify_openclaw',
'Send notification to OpenClaw Gateway',
{
format: z.enum(['confirm_request', 'phase_complete', 'confirm_reply', 'error']),
action: z.string().optional(),
command: z.string().optional(),
reason: z.string().optional(),
content: z.string().optional(),
risk_level: z.string().optional(),
error_type: z.string().optional(),
message: z.string().optional(),
session_id: z.string().optional(),
user_id: z.string().optional()
},
async ({ format, ...args }) => {
// 发送到 OpenClaw Gateway
const response = await fetch('http://localhost:4000/api/notify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ format, ...args })
});
return {
content: [{ type: 'text', text: 'Notification sent' }]
};
}
);
// 启动服务器
const transport = new StdioServerTransport();
await server.connect(transport);POST /api/notify
Content-Type: application/json
{
"format": "confirm_request",
"action": "RUN",
"command": "rm -rf node_modules",
"reason": "Cleaning dependencies",
"risk_level": "high",
"timestamp": "2026-02-10T16:00:00Z"
}
// 伪代码
async function handleNotify(request) {
const { format, ...data } = request.body;
switch (format) {
case 'confirm_request':
return sendConfirmRequest(data);
case 'phase_complete':
return sendPhaseComplete(data);
case 'confirm_reply':
return sendConfirmReply(data);
case 'error':
return sendError(data);
default:
return { status: 'ignored', reason: 'Unknown format' };
}
}
function sendConfirmRequest(data) {
const message = `🔴 RUN: ${data.command}\n\n` +
`原因: ${data.reason}\n` +
`风险: ${data.risk_level}\n\n` +
`请回复「RUN」或「REJECT」`;
return messageGateway.send({
channel: 'telegram',
target: '-5088311083',
message
});
}
function sendPhaseComplete(data) {
const message = `📋 PHASE_COMPLETE\n\n---\n\n${data.content}`;
return messageGateway.send({
channel: 'telegram',
target: '-5088311083',
message
});
}在运行 OpenCode 时设置:
export KIRO_OPENCLAW_URL="http://localhost:18789"
export KIRO_HOOK_TOKEN="your-secret-token"| 变量 | 说明 | 默认值 |
|---|---|---|
KIRO_OPENCLAW_URL |
OpenClaw Gateway 地址 | http://localhost:18789 |
KIRO_HOOK_TOKEN |
OpenClaw Hook Token (必填) | 无 |
在 ~/.config/opencode/opencode.json 中添加插件:
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"kiro-hook"
]
}在 ~/.config/openclaw/config.json 中添加 webhook 配置:
{
"hooks": {
"enabled": true,
"token": "your-secret-token",
"path": "/hooks"
}
}获取 OpenClaw Token:
# 查看当前配置
openclaw config get hooks.token测试 webhook:
# 测试发送消息
curl -X POST http://localhost:18789/hooks/agent \
-H "Authorization: Bearer your-secret-token" \
-H "Content-Type: application/json" \
-d '{"message": "测试消息", "name": "KiroHook", "sessionKey": "test", "deliver": true}'不需要额外的 MCP Server,直接使用 OpenClaw Webhook:
OpenCode Plugin (kiro-hook.js)
│
▼ HTTP POST
OpenClaw /hooks/agent
│
▼
Telegram (-5088311083)
创建 tmux session:
# 创建新 session
tmux new-session -s opencode -d
# 在 session 中运行 OpenCode
tmux send-keys -t opencode 'export KIRO_OPENCLAW_URL="http://localhost:18789"' Enter
tmux send-keys -t opencode 'export KIRO_HOOK_TOKEN="your-secret-token"' Enter
tmux send-keys -t opencode 'opencode' Enter
# 查看输出
tmux attach-session -t opencode或使用 systemd service(Linux):
[Unit]
Description=OpenCode with KiroHook
After=network.target
[Service]
Type=simple
User=napstablook
Environment=KIRO_OPENCLAW_URL=http://localhost:18789
Environment=KIRO_HOOK_TOKEN=your-secret-token
ExecStart=/usr/local/bin/opencode
Restart=on-failure
[Install]
WantedBy=multi-user.target1. 用户 (TG): "帮我初始化一个 Node.js 项目"
2. OpenClaw → OpenCode: "初始化一个 Node.js 项目"
3. OpenCode TUI:
- Sisyphus 规划任务
- 执行 npm init
- 安装依赖
- 创建基础文件
4. 第一个确认点:
🔴 RUN: npm install express mongoose
原因: Installing project dependencies 风险: medium
请回复「RUN」或「REJECT」
5. 用户 (TG): "RUN"
6. OpenClaw → OpenCode: RUN
7. OpenCode TUI 继续执行...
8. 阶段完成:
📋 PHASE_COMPLETE
已完成:
- ✅ 初始化 package.json
- ✅ 安装 express 和 mongoose
- ✅ 创建项目结构
请输入下一阶段的目标
9. 用户 (TG): "创建一个 REST API 框架"
10. 继续循环...
- 创建
kiro-hook.js插件框架 - 实现 event listener (session.idle, permission.asked, session.error)
- 实现 notifyOpenClaw HTTP 发送(使用 webhook)
- 实现 risk assessment(命令风险评估)
- 在 OpenClaw 配置中启用 webhook
- 生成并保存 hook token
- 测试 webhook 连通性
- 复制插件到
~/.config/opencode/plugins/ - 设置环境变量(KIRO_OPENCLAW_URL, KIRO_HOOK_TOKEN)
- 重启 OpenCode
- 测试完整交互流程
- 配置 tmux 保持 24/7 运行
- 模型切换限制: ACP 模式下无法切换模型,使用默认
big-pickle - 卷宗权限: 需要给 openclaw 用户完全磁盘访问权限
- Mac 休眠: 休眠后网络连接可能中断(但用户说不会断网)
- 实时性: 依赖 MCP Server 的 HTTP 响应速度