你将构建一个 Coding Agent,它:
1. 接受用户对 UI 组件的自然语言描述
2. 为 Claude Code 创建结构化上下文文件
3. Claude Code 在 workspace 中生成 React 组件
4. Next Hook 读取产出,保存并打开预览
## 目录结构
```
assistants/code-agent/
├── package.yao
├── prompts.yml
├── sandbox.yao
├── skills/
│ └── frontend-design/ ← 来自 Anthropic skills 仓库
│ └── SKILL.md
└── src/
└── index.ts
```
---
## 第一步:创建 `package.yao`
```json
{
"name": "Code Agent",
"connector": "$ENV.DEFAULT_CONNECTOR",
"guard": "bearer-jwt",
"optional": {
"history": true
}
}
```
---
## 第二步:创建 `prompts.yml`
Claude Code 将此作为 system prompt 读取。保持简洁 —— 真正的指令在 `context.md` 和 Skill 中。
```yaml
- role: system
content: |
你是一个 Coding Agent。
读取 $WORKDIR/context.md 了解你的任务。
将所有输出写入 $WORKDIR/output/。
完成后,将摘要写入 $WORKDIR/output/summary.md。
```
---
## 第三步:创建 `sandbox.yao`
```json
{
"version": "2.0",
"computer": {
"image": "yaoapp/tai-sandbox-claude:latest",
"memory": "2GB",
"cpus": 2
},
"runner": {
"name": "claude",
"options": {
"max_turns": 30,
"permission_mode": "bypassPermissions"
}
},
"lifecycle": "session",
"idle_timeout": "30m"
}
```
`skills/` 目录会在 Create Hook 运行前自动复制 —— Skills 无需添加 `prepare` 步骤。
---
## 第四步:添加 Frontend Design Skill
```bash
# 在应用根目录下执行
mkdir -p assistants/code-agent/skills
cp -r /path/to/anthropics-skills/skills/frontend-design assistants/code-agent/skills/
```
当用户要求构建前端组件时,Claude Code 会自动发现并应用该 Skill。
---
## 第五步:编写 Hook
```typescript
// assistants/code-agent/src/index.ts
import { agent, Process } from "@yao/runtime";
// ─── Create Hook ──────────────────────────────────────────────────────────
export function Create(
ctx: agent.Context,
messages: agent.Message[]
): agent.Create {
const input = messages[messages.length - 1]?.content || "";
// 清理上一次的输出
if (ctx.workspace.Exists("output")) {
ctx.workspace.RemoveAll("output");
}
ctx.workspace.MkdirAll("output");
// 为 Claude Code 写入上下文
ctx.workspace.WriteFile(
"context.md",
`# 任务
${input}
# 需求
- 将独立的 React 组件写入 \`output/Component.tsx\`
- 若需要 CSS,写入 \`output/Component.css\`
- 将简短的实现摘要写入 \`output/summary.md\`
- 不要安装包;只使用已有的内容
`
);
ctx.memory.context.Set("start_time", Date.now());
ctx.memory.context.Set("input", input);
return { messages };
}
// ─── Next Hook ────────────────────────────────────────────────────────────
export function Next(
ctx: agent.Context,
payload: agent.Payload
): agent.Next | null {
if (payload.error) {
ctx.Send(`错误:${payload.error}`);
return { data: { status: "error", message: payload.error } };
}
// 验证产出
if (!ctx.workspace.Exists("output/Component.tsx")) {
ctx.Send("Claude Code 未生成组件,请重试。");
return { data: { status: "no_output" } };
}
// 读取生成的文件
const component = ctx.workspace.ReadFile("output/Component.tsx");
const cssExists = ctx.workspace.Exists("output/Component.css");
const css = cssExists ? ctx.workspace.ReadFile("output/Component.css") : "";
const summary = ctx.workspace.Exists("output/summary.md")
? ctx.workspace.ReadFile("output/summary.md")
: "";
const duration =
Date.now() - (ctx.memory.context.Get("start_time") as number);
const input = ctx.memory.context.Get("input") as string;
// 保存到数据库
const record = Process("models.component.Save", {
name: extractComponentName(component),
input,
code: component,
css,
summary,
created_at: new Date().toISOString(),
}) as number;
// 展示摘要
if (summary) {
ctx.Send(`**完成,用时 ${duration}ms**\n\n${summary}`);
} else {
ctx.Send(`组件已生成,用时 ${duration}ms。`);
}
// 跳转到预览页面
if (record) {
ctx.Send({
type: "action",
props: {
name: "navigate",
payload: { route: `/agents/code-agent/preview?id=${record}` },
},
});
}
return { data: { status: "success", id: record } };
}
// ─── 辅助函数 ────────────────────────────────────────────────────────────
function extractComponentName(code: string): string {
const match = code.match(/export\s+(?:default\s+)?function\s+(\w+)/);
return match?.[1] || "Component";
}
```
---
## 第六步:定义 Component 模型
```json
// models/component.mod.yao
{
"name": "Component",
"table": { "name": "components" },
"columns": [
{ "name": "id", "type": "ID" },
{ "name": "name", "type": "string" },
{ "name": "input", "type": "text" },
{ "name": "code", "type": "text" },
{ "name": "css", "type": "text", "nullable": true },
{ "name": "summary", "type": "text", "nullable": true },
{ "name": "created_at", "type": "datetime" }
]
}
```
---
## 试用
1. 启动 Yao:`yao start`
2. 打开 Chat UI,进入 `code-agent` 助手
3. 输入:`构建一个带平滑动画的暗色模式切换按钮`
4. 观察执行步骤流式输出(每个 Claude Code 操作以 `execute` 消息形式显示)
5. 完成后,摘要出现,预览页面自动打开
---
## 执行流程
```
用户消息
↓
Runner.Prepare(框架 —— 在 Create Hook 之前)
→ 复制 skills/ → .yao/assistants/code-agent/skills/
↓
Create Hook
→ RemoveAll("output")
→ MkdirAll("output")
→ WriteFile("context.md", ...) ← Claude Code 的任务
→ memory.context.Set("start_time")
↓
Runner.Stream(Claude Code)
→ 读取 /workspace/context.md
→ 发现 frontend-design SKILL.md
→ 写入 output/Component.tsx
→ 写入 output/summary.md
↓
Next Hook
→ ReadFile("output/Component.tsx")
→ Process("models.component.Save", ...)
→ Send(摘要)
→ Send({ type: "action", 跳转到预览 })
```
---
## 下一步
你现在拥有一个完整的 CLI Agent。相同的模式适用于任何任务:将需求写入文件,让 Claude Code 执行,读取结果。添加更多 Skills、更丰富的提示词和自定义的 `sandbox.yao` 镜像,即可应对任何领域。