Adds a web-based UI for generating scene skill packages:
- Node.js HTTP server (zero npm dependencies) on port 3210
- HTML page with glass-morphism UI, dual-panel layout, settings modal
- LLM-powered scene-id/scene-name auto-extraction from directory contents
- Real-time SSE progress streaming during skill generation
- Spawns sg_scene_generate CLI with configurable parameters
- Windows-compatible startup scripts (serve.sh + serve.cmd)
- Rust integration tests for server files and HTML structure
Architecture:
Browser (HTML/JS) → Node.js server → LLM API + cargo run → sg_scene_generate
Files:
frontend/scene-generator/{server.js,config-loader.js,llm-client.js,generator-runner.js,sg_scene_generator.html,serve.sh,serve.cmd}
tests/{scene_generator_server_test.rs,scene_generator_html_test.rs,scene_generator_llm_test.js}
docs/superpowers/{plans,specs}/2026-04-16-scene-skill-generator*
🤖 Generated with [Qoder][https://qoder.com]
16 KiB
16 KiB
Scene Skill Generator — Design Document
Date: 2026-04-16 Status: Draft — awaiting review Author: Qoder
1. Goal
提供一个可视化界面,让用户选择场景目录后,自动通过大模型提取 scene-id 和 scene-name,配置输出路径和 lessons 文件,一键调用 sg_scene_generate 生成完整的 skill 包,并实时查看生成日志。
2. Architecture
┌─────────────────────────────────────────────────────────────────┐
│ sg_scene_generator.html (浏览器) │
│ ┌───────────────────┐ ┌───────────────────────────────┐ │
│ │ 左侧:操作面板 │ │ 右侧:实时日志流 │ │
│ │ │ │ │ │
│ │ 📂 选择场景目录 │───────│ [状态卡片 + 实时滚动日志] │ │
│ │ │ │ │ │
│ │ 自动填充字段: │ │ 分析场景目录... │ │
│ │ - scene-id │ │ 调用大模型提取场景信息... │ │
│ │ - scene-name │ │ scene-id: tq-lineloss-report │ │
│ │ │ │ scene-name: 台区线损报表 │ │
│ │ 可编辑字段: │ │ 生成 skill 包... │ │
│ │ - 输出根路径 │ │ 写入 SKILL.toml... │ │
│ │ - lessons 路径 │ │ 写入 browser_script... │ │
│ │ │ │ ✅ 生成完成 │ │
│ │ [⚙ 设置] │ └───────────────────────────────┘ │
│ │ [🚀 生成 Skill] │ │
│ └───────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ 1. POST /analyze (选择目录后自动触发)
│ → 发送目录路径 + 文件内容
│ 2. SSE /generate (点击生成按钮后触发)
│ → 推送实时进度
▼
┌─────────────────────────────────────────────────────────────────┐
│ server.js (Node.js, 默认端口 3210) │
│ │
│ POST /analyze │
│ 1. 读取 source-dir 下的关键文件 │
│ - scene.toml (如果存在) │
│ - *.js 脚本文件 │
│ - SKILL.md / SKILL.toml (如果存在) │
│ - 目录结构树 │
│ 2. 构造 prompt,调用 LLM API │
│ - baseUrl + apiKey + model 来自 sgclaw_config.json │
│ 3. 返回 JSON: { sceneId, sceneName } │
│ │
│ POST /generate │
│ 1. 接收 { sourceDir, sceneId, sceneName, outputRoot, lessons } │
│ 2. spawn: cargo run --bin sg_scene_generate \ │
│ --source-dir <sourceDir> \ │
│ --scene-id <sceneId> \ │
│ --scene-name <sceneName> \ │
│ --output-root <outputRoot> \ │
│ --lessons <lessons> │
│ 3. 通过 SSE 实时推送 stdout/stderr │
│ 4. 推送完成/失败事件 │
│ │
│ GET /health │
│ → { status: "ok", pid: 12345 } │
│ │
│ GET / │
│ → 服务 sg_scene_generator.html 静态文件 │
└─────────────────────────────────────────────────────────────────┘
│
│ LLM API (OpenAI-compatible format)
│ POST {baseUrl}/v1/chat/completions
▼
┌──────────────────────┐
│ LLM (DeepSeek) │
│ │
│ System: 你是一个场景 │
│ 信息提取助手... │
│ User: 以下是场景目录 │
│ 内容... 请提取 │
│ scene-id 和 │
│ scene-name │
└──────────────────────┘
3. File Map
新建文件
| 文件 | 说明 |
|---|---|
frontend/scene-generator/sg_scene_generator.html |
主页面,内联 CSS + JS,复用 service-console 设计风格 |
frontend/scene-generator/server.js |
Node.js 轻量 HTTP 服务器(零外部依赖) |
frontend/scene-generator/serve.sh |
一键启动脚本(Windows 兼容) |
frontend/scene-generator/serve.cmd |
Windows 一键启动脚本 |
frontend/scene-generator/config-loader.js |
读取并解析 sgclaw_config.json |
frontend/scene-generator/llm-client.js |
封装 LLM API 调用(OpenAI-compatible 格式) |
frontend/scene-generator/generator-runner.js |
封装 sg_scene_generate 子进程调用 + SSE 推送 |
引用文件(不修改)
| 文件 | 用途 |
|---|---|
src/bin/sg_scene_generate.rs |
被 server.js 通过 cargo run 调用 |
src/generated_scene/generator.rs |
理解生成逻辑和输出结构 |
sgclaw_config.json |
读取 LLM 连接配置(apiKey, baseUrl, model) |
docs/superpowers/references/tq-lineloss-lessons-learned.toml |
默认 lessons 路径 |
frontend/service-console/sg_claw_service_console.html |
UI 风格参考 |
4. UI Design
4.1 整体布局
复用 service-console 的双栏布局:
- 外层容器 (
.shell):圆角玻璃拟态面板,与 service-console 共享 CSS 变量 - 顶部 (
.hero):标题 "场景 Skill 生成器" + 简短说明 - 内容区 (
.content):grid双栏,左侧操作面板 + 右侧日志流
4.2 左侧操作面板
场景目录选择区
📂 场景目录
[ 粘贴或输入路径 ____________________________ ] [ 浏览 📁 ]
当前:D:\data\ideaSpace\rust\sgClaw\claw-new\examples\generated_scene_platform\scenarios\tq-lineloss-report
使用文本输入框 + "浏览" 按钮。点击 "浏览" 时,前端调用 POST /browse,由 Node.js 弹出系统目录选择对话框(通过 electron 风格的 open-dialog 不可行 — 改为用户在输入框中粘贴/输入路径,服务端通过 fs.stat 校验路径合法性)。
为简化实现,采用更务实的方案:
- 主输入框:用户粘贴或手动输入场景目录的绝对路径
- 输入路径后按回车或点击 "分析" 按钮,触发
/analyze请求 - 服务端通过
fs.statSync(sourceDir).isDirectory()校验路径
可选增强:如果 Node.js 安装了 electron,可通过 dialog.showOpenDialog 弹出系统选择框,但这会增加依赖。默认不采用。
自动提取结果(只读展示,可手动修正)
scene-id
tq-lineloss-report
scene-name
台区线损报表
分析中显示 loading 状态,分析失败时可手动输入。
设置按钮
点击弹出模态框,包含以下字段:
| 字段 | 默认值 | 说明 |
|---|---|---|
| 输出根路径 | D:/data/ideaSpace/rust/sgClaw/claw-new/examples/generated_scene_platform |
skill 包输出根目录,实际输出到 <output-root>/skills/<scene-id>/ |
| Lessons 路径 | D:/data/ideaSpace/rust/sgClaw/claw-new/docs/superpowers/references/tq-lineloss-lessons-learned.toml |
lessons TOML 文件路径 |
| LLM 服务地址 | 来自 sgclaw_config.json 的 baseUrl |
可覆盖 |
| LLM 模型 | 来自 sgclaw_config.json 的 model |
可覆盖 |
| Node 服务端口 | 3210 |
server.js 监听端口 |
生成按钮
[ 🚀 生成 Skill ] (disabled 直到选择了目录且提取完成)
4.3 右侧日志流
与 service-console 一致的流式日志展示:
- 空状态:显示提示 "选择场景目录开始生成"
- status 行:关键阶段标记("开始分析", "提取完成", "开始生成", "生成成功")
- log 行:cargo run 的 stdout 输出
- error 行:stderr 输出或错误信息
- complete 行:最终结果,包含生成的 skill 包路径
4.4 状态卡片
左侧面板顶部显示当前状态:
[●] 就绪 / 分析中 / 生成中 / 完成 / 错误
颜色编码:
- 就绪:灰色
- 分析中:橙色
- 生成中:青色(accent)
- 完成:绿色
- 错误:红色
5. API Design
5.1 POST /analyze
请求体:
{
"sourceDir": "D:/data/ideaSpace/rust/sgClaw/claw-new/examples/generated_scene_platform/scenarios/tq-lineloss-report"
}
服务端自行读取目录内容:
- 校验路径是否存在且为目录
- 读取
scene.toml(如果存在) - 读取
*.js脚本文件 - 读取
SKILL.md/SKILL.toml(如果存在) - 生成目录结构树
响应:
{
"sceneId": "tq-lineloss-report",
"sceneName": "台区线损报表"
}
LLM Prompt 设计:
System: 你是一个场景信息提取助手。根据场景目录的内容,提取 scene-id 和 scene-name。
scene-id 规则:
- 使用英文短横线连接,如 tq-lineloss-report
- 全小写,有业务含义
scene-name 规则:
- 使用中文,简短描述性名称
- 如 "台区线损报表"、"知乎热榜导出"
User: 以下是场景目录的内容:
=== scene.toml ===
[scene content here]
=== 脚本文件 ===
[script content here]
=== 目录结构 ===
[file tree here]
请以 JSON 格式返回:{"sceneId": "...", "sceneName": "..."}
5.2 POST /generate (SSE)
请求体:
{
"sourceDir": "/path/to/scenario/dir",
"sceneId": "tq-lineloss-report",
"sceneName": "台区线损报表",
"outputRoot": "/path/to/output/root",
"lessons": "/path/to/lessons.toml"
}
SSE 事件流:
event: status
data: {"message": "开始生成 skill 包..."}
event: status
data: {"message": "调用 sg_scene_generate..."}
event: log
data: {"message": "generated scene package: ..."}
event: complete
data: {"success": true, "skillRoot": "/path/to/skills/tq-lineloss-report"}
或
event: error
data: {"message": "生成失败: ..."}
5.3 GET /health
响应:
{
"status": "ok",
"pid": 12345,
"configLoaded": true,
"configPath": "D:/data/ideaSpace/rust/sgClaw/sgclaw_config.json"
}
6. Server Design (server.js)
6.1 模块结构
server.js — HTTP 路由入口,SSE 连接管理
config-loader.js — 读取 sgclaw_config.json,暴露 LLM 配置 + projectRoot
llm-client.js — 调用 LLM API,返回 JSON 提取结果
generator-runner.js — spawn 子进程,通过 SSE 推送输出
6.1.1 projectRoot 配置
cargo run --bin sg_scene_generate 需要在项目根目录下执行。projectRoot 的确定优先级:
- 环境变量
SGCLAW_PROJECT_ROOT(最高优先级) sgclaw_config.json同级目录(常见情况:配置文件在项目根目录)- 启动脚本所在目录
6.2 零依赖原则
仅使用 Node.js 内置模块:
http— HTTP 服务器fs— 文件读取path— 路径处理child_process— 子进程调用events— 事件发射
6.3 启动流程
1. 读取 sgclaw_config.json (路径通过环境变量 SGCLAW_CONFIG_PATH 或默认 ../sgclaw_config.json)
2. 验证必需字段: apiKey, baseUrl, model
3. 启动 HTTP 服务器,监听 0.0.0.0:3210
4. 打印启动信息,包含访问地址
6.4 错误处理
| 场景 | 处理方式 |
|---|---|
| sgclaw_config.json 不存在 | 启动失败,提示用户设置环境变量 |
| LLM API 调用失败 | 返回 502 + 错误信息,前端允许手动输入 |
| cargo run 失败 | SSE 推送 error 事件,显示 stderr |
| source-dir 不存在 | 返回 400 |
| 端口被占用 | 启动失败,提示更换端口 |
7. Security Considerations
- 仅监听 localhost:server.js 默认绑定
127.0.0.1,不暴露到外部网络 - API Key 不暴露给前端:LLM API 调用完全在 Node.js 服务端完成,前端不接触 API Key
- 路径校验:
sourceDir和outputRoot需做基本路径合法性检查,防止路径遍历攻击 - 子进程超时:
cargo run设置 5 分钟超时,防止挂起
8. Default Configuration
| 配置项 | 默认值 | 来源 |
|---|---|---|
| LLM apiKey | sgclaw_config.json 中的 apiKey |
启动时读取 |
| LLM baseUrl | sgclaw_config.json 中的 baseUrl |
启动时读取 |
| LLM model | sgclaw_config.json 中的 model |
启动时读取 |
| 默认 lessons 路径 | docs/superpowers/references/tq-lineloss-lessons-learned.toml |
项目约定 |
| 默认输出根路径 | examples/generated_scene_platform |
项目约定 |
| Node 服务端口 | 3210 |
硬编码,可配置 |
9. User Flow
1. 用户运行 bash serve.sh (或 node server.js)
2. 浏览器打开 http://127.0.0.1:3210
3. 页面加载,显示 "就绪" 状态
4. 用户在 "场景目录" 输入框中粘贴或输入绝对路径
5. 用户点击 "分析" 按钮(或输入框回车),触发 /analyze 请求
6. server.js 读取目录内容,调用 LLM 提取 scene-id/name
7. 页面自动填充 scene-id 和 scene-name 字段
8. 用户确认/修改字段,点击 "设置" 检查输出路径和 lessons
9. 用户点击 "生成 Skill"
10. server.js 通过 SSE 推送实时进度
11. 页面右侧日志流展示生成过程
12. 生成完成,显示 skill 包路径
13. 用户可前往输出目录查看生成的 skill
10. Windows Compatibility
由于目标平台是 Windows:
serve.sh同时提供serve.cmd替代方案- 路径分隔符统一使用
/(Node.jspath模块自动处理) cargo run命令在 Windows 上同样可用- 路径输入框支持 Windows 格式路径(如
D:\data\ideaSpace\...) - 服务端自动将
\转换为/以兼容 Rust CLI 参数
11. Future Extensions (Not in Scope)
- 批量生成:一次选择多个场景目录
- 生成后自动注册到 scene.toml manifest
- 生成后自动运行 skill 测试
- 历史记录:保存之前的生成记录
- 生成参数模板:保存常用的输出路径/lessons 组合