@aweeclaw/scenario-cli
v1.0.4
Published
CLI tool for building, packing, and managing Aweeclaw scenarios
Readme
@aweeclaw/scenario-cli
AweeClaw 场景开发 CLI 工具,用于初始化、构建、打包和校验 AweeClaw 场景包。
场景开发者使用此工具将 TypeScript + React 代码预编译为 ESM Bundle,客户端运行时动态加载。共享依赖(React、zustand、lucide-react 等)由宿主应用注入,无需打包进场景包。
安装
# 全局安装
npm install -g @aweeclaw/scenario-cli
# 或使用 npx(无需安装)
npx @aweeclaw/scenario-cli init my-scenario要求 Node.js >= 18.0.0。
快速开始
# 1. 初始化场景项目
npx aweeclaw-scenario init my-scenario --name-zh "我的场景" --category development
# 2. 进入项目并安装依赖
cd my-scenario
npm install
# 3. 启动开发服务器
npx aweeclaw-scenario dev
# 4. 构建场景包
npx aweeclaw-scenario build
# 5. 校验构建产物
npx aweeclaw-scenario validate
# 6. 打包为 tar.gz
npx aweeclaw-scenario pack
# → my-scenario-1.0.0.tar.gz命令详解
init — 初始化场景项目
创建场景脚手架,生成 scenario.json、TypeScript 配置、入口文件和组件模板。
aweeclaw-scenario init <name> [options]参数:
| 参数 | 说明 |
|------|------|
| <name> | 场景名称,自动转为小写 kebab-case 作为 ID |
选项:
| 选项 | 默认值 | 说明 |
|------|--------|------|
| --name-zh <nameZh> | 同 name | 场景中文名称 |
| --category <category> | custom | 场景分类 |
| --type <type> | programmatic | 场景类型:declarative 或 programmatic |
| --author <author> | unknown | 作者名称 |
分类可选值: productivity、development、education、business、creative、data、health、finance、legal、custom
示例:
# 编程式场景(默认)
aweeclaw-scenario init code-reviewer --name-zh "代码审查" --category development --author "zhangsan"
# 声明式场景
aweeclaw-scenario init legal-advisor --name-zh "法律顾问" --type declarative --category legalbuild — 构建场景包
编程式场景使用 Vite 构建 ESM Bundle,声明式场景直接复制文件。构建产物输出到 dist/ 目录。
aweeclaw-scenario build [options]选项:
| 选项 | 默认值 | 说明 |
|------|--------|------|
| --out-dir <dir> | dist | 输出目录 |
| --minify | false | 压缩输出 |
| --no-sourcemap | 启用 | 禁用 sourcemap |
构建产物结构(编程式):
dist/
├── manifest.json # 包清单(含 checksum)
├── config/
│ └── scenario.json # 场景配置
├── bundle/
│ ├── index.js # ESM 主入口
│ ├── DashboardPanel.js # 组件 chunk(按需加载)
│ ├── index.js.map # Sourcemap
│ └── *.css # 样式文件
├── prompts/
│ └── system.md
└── assets/ # 静态资源(如有)构建产物结构(声明式):
dist/
├── manifest.json
├── config/
│ └── scenario.json
├── prompts/
│ ├── system.md
│ └── workflow.md
├── scripts/
│ ├── onActivate.js
│ └── onDeactivate.js
└── assets/pack — 打包发布
将 dist/ 目录打包为 {id}-{version}.tar.gz,用于上传到场景市场。
aweeclaw-scenario pack [options]选项:
| 选项 | 默认值 | 说明 |
|------|--------|------|
| --out-dir <dir> | . | tar.gz 输出目录 |
打包前会自动更新 manifest.json 中的 checksum。
示例:
aweeclaw-scenario pack
# → my-scenario-1.0.0.tar.gz
aweeclaw-scenario pack --out-dir ./releases
# → releases/my-scenario-1.0.0.tar.gzdev — 本地开发
启动 Vite 开发服务器,支持热更新。
aweeclaw-scenario dev [options]选项:
| 选项 | 默认值 | 说明 |
|------|--------|------|
| --port <port> | 3210 | 开发服务器端口 |
声明式场景无需开发服务器,会提示直接编辑文件。
validate — 校验场景包
校验 dist/ 目录中的构建产物,检查完整性和正确性。
aweeclaw-scenario validate [options]选项:
| 选项 | 默认值 | 说明 |
|------|--------|------|
| --strict | false | 启用严格校验模式 |
校验项:
scenario.json格式和字段校验(Zod Schema)manifest.json完整性- 编程式场景的
bundle/index.js是否存在 - 共享依赖是否正确外部化
- 声明式场景的文件引用完整性(identity / database / scripts)
- SHA-256 checksum 是否匹配
- 包大小是否超过 50 MB 限制
严格模式下会额外检查 __AWEECLAW_SHARED__ 引用。
scenario.json 配置说明
场景的核心配置文件,位于项目根目录。
编程式场景配置
{
"id": "my-scenario",
"version": "1.0.0",
"name": "My Scenario",
"nameZh": "我的场景",
"description": "A brief description",
"descriptionZh": "简要描述",
"author": "author-name",
"icon": "Package",
"category": "development",
"tags": ["code", "review"],
"license": "MIT",
"type": "programmatic",
"entryPoint": "src/index.ts",
"permissions": [],
"sharedDeps": {
"react": "^18.3.0",
"react-dom": "^18.3.0",
"zustand": "^5.0.0",
"lucide-react": "^0.562.0"
},
"dependencies": []
}声明式场景配置
{
"id": "travel-planner",
"version": "1.0.0",
"name": "Travel Planner",
"nameZh": "旅行规划师",
"description": "AI travel planning assistant",
"descriptionZh": "AI 旅行规划助手",
"author": "aweeclaw",
"icon": "Plane",
"category": "productivity",
"tags": ["travel", "planning"],
"type": "declarative",
"identity": {
"systemPromptFile": "prompts/system.md",
"workflowFile": "prompts/workflow.md"
},
"capabilities": {
"builtinTools": ["web_search", "ask_user", "remember"],
"customTools": [
{
"name": "search_flights",
"description": "Search for flights",
"parameters": {
"origin": { "type": "string", "description": "Departure city", "required": true },
"destination": { "type": "string", "description": "Arrival city", "required": true }
},
"executor": "web_search",
"template": "search flights from {origin} to {destination}"
}
]
},
"ui": {
"layout": "chat-centric"
},
"database": {
"installScriptFiles": ["db/install.sql"],
"uninstallScriptFiles": ["db/uninstall.sql"]
},
"scripts": {
"onActivateFile": "scripts/onActivate.js",
"onDeactivateFile": "scripts/onDeactivate.js"
}
}字段说明
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | string | ✅ | 场景唯一标识,小写字母数字+连字符 |
| version | string | ✅ | 语义化版本号(如 1.0.0) |
| name | string | ✅ | 英文名称 |
| nameZh | string | ✅ | 中文名称 |
| description | string | | 英文描述 |
| descriptionZh | string | | 中文描述 |
| author | string | | 作者 |
| icon | string | | 图标名称(lucide-react 图标) |
| category | enum | | 分类 |
| tags | string[] | | 标签 |
| license | string | | 开源协议 |
| type | enum | ✅ | declarative 或 programmatic |
| entryPoint | string | | 编程式场景入口文件 |
| permissions | string[] | | 所需权限声明 |
| sharedDeps | object | | 编程式场景共享依赖及版本要求 |
| dependencies | array | | 场景间依赖 |
| identity | object | | 声明式场景身份配置 |
| capabilities | object | | 声明式场景能力声明 |
| ui | object | | 声明式场景 UI 配置 |
| database | object | | 声明式场景数据库脚本 |
| scripts | object | | 声明式场景生命周期脚本 |
category 可选值
| 值 | 说明 |
|----|------|
| productivity | 生产力 |
| development | 开发工具 |
| education | 教育 |
| business | 商业 |
| creative | 创意 |
| data | 数据 |
| health | 健康 |
| finance | 金融 |
| legal | 法律 |
| custom | 自定义 |
两种场景模式
编程式(Programmatic)
使用 TypeScript + React 开发,支持自定义 UI 组件、完整 JS 逻辑。构建时通过 Vite 编译为 ESM Bundle。
适用场景: 需要自定义面板、复杂交互、数据可视化等
项目结构:
my-scenario/
├── scenario.json
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # 入口,导出 ScenarioModule
│ └── components/
│ └── DashboardPanel.tsx
├── prompts/
│ └── system.md
└── assets/入口文件模板:
import type { ScenarioModule, ScenarioModuleContext } from '@aweeclaw/scenario-sdk'
import { DashboardPanel } from './components/DashboardPanel.js'
export default {
id: 'my-scenario',
version: '1.0.0',
getManifest() { /* ... */ },
getPlugin() { /* ... */ },
getTools() { return [] },
getComponents() { return { DashboardPanel } },
async onActivate(context: ScenarioModuleContext) {
context.publishData('scenario:activated', { scenarioId: 'my-scenario' })
},
async onDeactivate(context: ScenarioModuleContext) {
context.publishData('scenario:deactivated', { scenarioId: 'my-scenario' })
},
} satisfies ScenarioModule声明式(Declarative)
使用 JSON 配置 + Markdown 提示词 + 沙箱脚本,无需编译。
适用场景: 简单对话场景、提示词模板、无自定义 UI
项目结构:
my-scenario/
├── scenario.json
├── package.json
├── prompts/
│ ├── system.md
│ └── workflow.md
├── scripts/
│ ├── onActivate.js
│ └── onDeactivate.js
├── db/
│ ├── install.sql
│ └── uninstall.sql
└── assets/左侧菜单配置
左侧菜单(NavigationRail)是场景 UI 的核心导航区域。通过 ui.sidebarItems 配置菜单项,每个菜单项包含图标、标签和对应的面板组件。
工作原理
sidebarItems 配置 → NavigationRail 渲染图标 → 用户点击 → 侧边栏显示对应面板- 场景激活时,客户端读取
ui.sidebarItems渲染左侧菜单图标 - 用户点击图标,客户端设置
activeSidePanel为该菜单项的id - 侧边栏根据
id查找面板组件并渲染
内置面板组件
客户端提供了以下内置面板,可直接在 sidebarItems 中引用:
| component 值 | 说明 |
|-------------|------|
| ExplorerView | 文件浏览器 |
| SearchView | 全局搜索 |
| GitView | Git 源码管理 |
| ProblemsView | 问题诊断 |
| OutlineView | 符号大纲 |
| HistoryView | 历史记录 |
| ShellView | Shell 终端 |
| KnowledgeView | 知识库 |
| NotesView | 笔记 |
| TasksView | 任务管理 |
| BookmarksView | 书签 |
| PromptsView | 提示词库 |
声明式场景配置菜单
在 scenario.json 的 ui.sidebarItems 中配置,仅支持内置面板:
{
"ui": {
"layout": "chat-centric",
"sidebarItems": [
{
"id": "explorer",
"icon": "Files",
"label": "Workspace",
"labelZh": "工作区",
"component": "ExplorerView",
"position": 0
},
{
"id": "knowledge",
"icon": "BookOpen",
"label": "Knowledge",
"labelZh": "知识库",
"component": "KnowledgeView",
"position": 1
},
{
"id": "history",
"icon": "History",
"label": "History",
"labelZh": "历史",
"component": "HistoryView",
"position": 2
}
]
}
}编程式场景配置菜单
编程式场景支持自定义面板组件,分两步:
第一步:在 getPlugin() 的 ui.sidebarItems 中声明菜单项
getPlugin() {
return {
// ...其他字段
ui: {
layout: 'dashboard-centric',
panels: [
{ id: 'dashboard', component: 'DashboardPanel', region: 'primary', defaultVisible: true, resizable: true },
{ id: 'sidebar', component: 'Sidebar', region: 'secondary', defaultVisible: true, resizable: true, minWidth: 220, maxWidth: 400 },
{ id: 'chat', component: 'ChatPanel', region: 'auxiliary', defaultVisible: true, resizable: true, minWidth: 300, maxWidth: 800 },
],
sidebarItems: [
// 自定义面板:component 对应 getComponents() 返回的 key
{ id: 'tasks', icon: 'CheckSquare', label: 'Tasks', labelZh: '任务', component: 'TaskListPanel', position: 0 },
{ id: 'stats', icon: 'BarChart3', label: 'Stats', labelZh: '统计', component: 'StatsPanel', position: 1 },
// 内置面板:直接引用内置组件名
{ id: 'explorer', icon: 'Files', label: 'Workspace', labelZh: '工作区', component: 'ExplorerView', position: 2 },
{ id: 'knowledge', icon: 'BookOpen', label: 'Knowledge', labelZh: '知识库', component: 'KnowledgeView', position: 3 },
],
statusBarItems: [],
defaultSidePanel: 'tasks',
},
}
}第二步:在 getComponents() 中注册自定义组件
import { TaskListPanel } from './components/TaskListPanel.js'
import { StatsPanel } from './components/StatsPanel.js'
getComponents() {
return {
TaskListPanel,
StatsPanel,
}
}关键:
sidebarItems中的component值必须与getComponents()返回的 key 一致。客户端的PanelRegistry会自动将两者关联。
sidebarItems 字段说明
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | string | ✅ | 菜单项唯一标识,同时作为面板 ID |
| icon | string | ✅ | 图标名称,使用 lucide-react 图标名(如 Files、BookOpen、CheckSquare) |
| label | string | ✅ | 英文标签 |
| labelZh | string | ✅ | 中文标签 |
| component | string | ✅ | 面板组件名,内置面板用内置名,自定义面板用 getComponents() 中的 key |
| position | number | | 排序位置,从小到大 |
| wideMode | boolean | | 宽屏模式下是否作为主面板显示(隐藏侧边栏) |
常用图标参考
| 图标名 | 适用场景 |
|--------|---------|
| Files | 文件/工作区 |
| Search | 搜索 |
| GitBranch | Git 管理 |
| BookOpen | 知识库 |
| History | 历史记录 |
| Terminal | 终端 |
| CheckSquare | 任务 |
| BarChart3 | 统计/图表 |
| Database | 数据源 |
| Settings | 设置 |
| Map | 地图/行程 |
| Wallet | 预算/财务 |
| Stethoscope | 诊断 |
| Brain | AI/深度思考 |
| Zap | 快速操作 |
| FolderTree | 目录树 |
| ListTree | 大纲 |
| AlertCircle | 问题/告警 |
完整图标列表参考 lucide.dev
布局与菜单的关系
不同 layout 值对左侧菜单的显示策略不同:
| 布局 | 菜单显示 | 说明 |
|------|---------|------|
| chat-centric | 有 sidebarItems 时显示 | 无菜单项时隐藏侧边栏 |
| editor-centric | 始终显示 | 左侧菜单 + 中间编辑器 + 右侧聊天 |
| dashboard-centric | 始终显示 | 左侧菜单 + 中间仪表盘 + 右侧聊天 |
| analytics-centric | 始终显示 | 左侧菜单 + 中间分析面板 + 右侧聊天 |
| canvas-centric | 始终显示 | 左侧菜单 + 中间画布 + 右侧聊天 |
| minimal | 不显示 | 仅聊天界面 |
| focus-centric | 不显示 | 编辑器 + 聊天 |
| research-centric | 有 sidebarItems 时显示 | 编辑器 + 聊天 + 可选菜单 |
共享依赖机制
编程式场景的 React、zustand、lucide-react 等依赖不打包进场景包,而是由宿主应用通过全局变量 __AWEECLAW_SHARED__ 注入。
构建时行为:
- 这些依赖被标记为
external,不会打包进 bundle - import 语句被重写为全局变量引用(如
react→__AWEECLAW_SHARED__.modules.react)
运行时行为:
- 宿主应用启动时注入共享依赖到
window.__AWEECLAW_SHARED__ - 场景加载时直接从全局变量获取,避免版本冲突和 React hooks 崩溃
当前共享依赖版本:
| 依赖 | 版本 | |------|------| | react | ^18.3.0 | | react-dom | ^18.3.0 | | react/jsx-runtime | ^18.3.0 | | zustand | ^5.0.0 | | lucide-react | ^0.562.0 |
开发指南
本地开发
# 克隆仓库
git clone <repo-url>
cd aweeclaw-scenario-cli
# 安装依赖
npm install
# 构建
npm run build
# 开发模式(监听文件变化)
npm run dev
# 测试 CLI
node dist/bin.js --help发布
npm run build
npm publish --access public常见问题
Q: 场景中如何使用 React hooks?
场景使用宿主应用提供的 React 实例,hooks 调用与宿主应用在同一上下文,不会出现 "Invalid hook call" 错误。只需正常编写 React 组件即可。
Q: 场景可以使用哪些 UI 库?
场景可以使用宿主应用提供的共享依赖(React、zustand、lucide-react)。其他 UI 库需要自行打包进场景包,但需注意体积控制(建议 < 50 MB)。
Q: 如何调试场景?
- 使用
aweeclaw-scenario dev启动本地开发服务器 - 在宿主应用中通过本地路径加载场景
- 使用浏览器 DevTools 调试
Q: 编程式和声明式如何选择?
- 需要自定义 UI 面板 → 编程式
- 只需对话提示词模板 → 声明式
- 不确定 → 先用声明式,后续可迁移为编程式
License
MIT
