用对话把用户的想法做成一个真实可访问的网站并一键发布上线。当用户说"做一个网站/页面/门户/展示站/落地页/看板/H5 并要能打开访问""帮我搭个站""把这份内容做成网页发布出去",或要在已发布站点上继续迭代修改时,或要查看/修改站点已存的 KV 数据(站内页面写入的计数、配置、内容)时,务必使用本技能。它教你两条建站路径——简单内容手写静态站、复杂/精美需求用预装的 React 工程模板构建——并用 choose_design 让用户三选一设计方案,最后用 publish_site 发布为平台托管站点,拿到形如 /site/<slug>/ 的访问链接交付给用户。
Install
npx skillscat add zju-real/hugagentos/site-builder Install via the SkillsCat registry.
对话建站(Site Builder)
本技能教你把用户的需求做成一个完整可访问的网站,发布后返回一个可直接打开的链接
(形如 /site/<slug>/)。发布能力由「站点」插件的 site_publish MCP 提供。
桌面本机/混合模式:先在本地项目中保存源码,再构建发布。
本地项目提示词给出的真实绝对路径优先于本文的 /workspace 和 /myspace 示例。
在本地项目的 sites/<站点名>/ 中初始化、编写和修改源码;新站点入口会先准备本地项目。
静态站显式传 src_dir 指向这个页面目录;React 站传项目内 source_dir 和实际构建输出 src_dir。
云端仅接收托管文件,本机保存站点编号与源码关联。编辑时严格使用当前站点上下文的
source_dir / publish_dir / site_id;不重新建站、不去旧会话临时目录或 /myspace 找源码。云端模式:发布后平台会自动为站点建一个同名"项目"并把源码文件存进去(后端自动完成)。
用户随后可在「实验室 → 站点」点站点卡片的「编辑」按钮,通过对话继续修改。
沟通原则(面向非技术用户)
把用户当成不懂技术的知识工作者:
- 谈他的站点——内容、样式、进度、访问方式;不要在给用户的消息里出现
vite / npm / build / node_modules / 构建产物 / 目录路径 这类词,改说
「正在搭建页面」「正在生成站点」「即将发布」。 - 每个阶段(准备设计方案 / 搭建 / 发布)至多一句简短进度播报。
- 可恢复的技术问题自己默默重试解决,不向用户展示报错细节;只有真正需要用户
决策或提供信息时才提问。
第一步:选路径(先判断,再动手)
| 用哪条 | 判据 |
|---|---|
| A. 静态站 fast path | 单页内容展示、通知/介绍/菜单类落地页、需求简单明确、无组件化交互诉求 |
| B. React 工程 capability path | 多页面应用、交互组件、图表/数据看板、用户要求"好看/精致/App 感"、预期持续迭代 |
拿不准时:内容型选 A,产品型选 B。编辑已有站点时不换路径(见编辑一节)。
路径 A:静态站(保持简单)
- 生成完整静态站(用 write / bash):
- 放在
/workspace/site/下(项目会话则直接在项目文件夹里); - 必须有
index.html入口;可以有多页面、子目录、css/、js/、图片; - 样式/脚本尽量内联或本地化——外部 CDN 在内网环境可能加载不到。
- 放在
- 发布:
publish_site(title='站点名称', src_dir='<实际页面目录>')。必须明确页面根目录,不能默认上传整个项目。 - 交付:把返回
url以 markdown 链接发给用户(管理入口见「交付话术」)。
路径 B:React 工程(复杂/精美站点)
B1. 初始化(一条命令,秒级完成)
bash "${SITE_TEMPLATE_HOME:-/opt/site-template}/init-react-site.sh" /workspace/site-src/<英文短名>模板预装 React 18 + antd + echarts + tailwind v4 + lucide/motion/dayjs,依赖开箱即用。
脚本幂等,可反复执行(重开会话后也先跑它自愈环境);它会打印本工程的构建
命令、产物目录与发布参数,后续步骤以其输出为准。
B2. 设计三选一(choose_design,新建站点必做)
在动手写正式代码之前,让用户从 3 个设计方向里挑一个:
- 若用户需求里关键信息缺失(受众/用途/内容/风格偏好),先用一条消息问清
1-2 个最关键的问题;用户已给出完整方向或明确说"你定"则跳过。 - 写 3 个风格迥异的单文件 mockup 到
/workspace/design_options/{a,b,c}.html:- 每个自包含(内联 CSS,不引外部资源),用真实感内容(真实标题/数据/文案,
不要 lorem ipsum),三个方案用相同内容、只比设计(配色/布局/字体/密度); - 避免自绘 SVG 插画——用排版、色彩、CSS 形状表达设计感。
- 每个自包含(内联 CSS,不引外部资源),用真实感内容(真实标题/数据/文案,
- 逐个截图并登记:
每张图调npx playwright screenshot --viewport-size=1280,900 \ file:///workspace/design_options/a.html /workspace/design_options/a.pngsandbox_get_artifact(src_path='/workspace/design_options/a.png', name='design-a.png')拿file_id。这些截图是选择器素材,不要 pin_to_workspace。 - 调
choose_design(question='您喜欢哪种设计风格?', options=[{id, title, brief, image_file_id}, ...])——工具会挂起等用户在界面上点选(等多久都正常)。 - 拿到返回后严格按选中方案展开:布局/配色/字体以该 mockup 为准,不混入其它
方案元素;用户跳过或超时则选你最推荐的方案并向用户说明一句。
编辑已有站点、或用户已给出完整明确的设计要求时,跳过本步骤。
B3. 实现 → 构建 → 发布
- 按选定方案改
src/(页面放src/pages/,路由表src/App.jsx),并把index.html的<title>改成真实站点名(模板占位是"站点建设中",留着会
显示在用户浏览器标签页上)。工程硬约束:- HashRouter 不许换、
vite.config.mjs的base: './'不许改(改了发布后打不开); - 禁外部 CDN;静态资源放
src/assets/交给构建打包; - 动态数据用
src/lib/siteApi.js(kvGet/kvSet/kvDelete/submitForm); - 界面图标用预装图标库(
lucide-react或@ant-design/icons),禁止拿
emoji 当图标(跨系统渲染不一致、色相杂乱,是"AI 生成感"最强的元素); - echarts 图表注意数值标签防裁切:柱状图外置 label 要给
grid.top留够
空间或适当抬高yAxis.max;轴刻度单位归口到标题一处,不要每个刻度都带; - 优先用预装库(antd/echarts/…);确需新依赖:编辑
package.json后重跑
init 脚本(此时会物化独立依赖副本并增量安装,可能需要几分钟——bash 调用
记得给足超时),禁止在工程目录里直接npm install。
- HashRouter 不许换、
- 构建:
cd /workspace/site-src/<名> && npm run build(产物自动落/workspace/.site-dist/<名>/)。构建报错先修再继续,禁止带错发布。 - 发布前自检清单(逐条过,都是高频遗漏):
index.html的<title>已改成真实站点名(不是"站点建设中");- 图表上的统计标注(均值线/合计等)由数据计算得出,不是拍脑袋写死的数;
- 涨跌/同比类徽章按数据符号驱动(负增长要红色下箭头,不能写死绿色上箭头);
- y 轴不写死 min/max(换数据会裁剪出图),柱宽用
barMaxWidth而非固定值; - 删掉未使用的 import 和死变量;重复样式抽成常量或小组件。
- 发布(两个参数都必传):
产物进托管,源码工程镜像进项目文件夹(保证「编辑」进来的是可改的源码)。publish_site(title='站点名', src_dir='/workspace/.site-dist/<名>', source_dir='/workspace/site-src/<名>')
漏传source_dir会导致项目里的源码被产物覆盖——绝不允许。 - 尽早发第一版:完成核心页面即可先发布(发布后源码即进项目保险箱,
不受沙箱回收影响),再迭代细节发新版。
编辑已有站点(项目入口、新会话、原会话和编辑按钮统一)
编辑必须显式传原 site_id 和准确 src_dir。不依赖按钮或会话替你补参数。
只有用户明确要求新建另一个站点时才省略 site_id。
- 先查目标:调用
list_sites(),获取当前账号可修改站点的
site_id、title、url、version、kind、source_dir、publish_dir、in_current_project。
本机和云端都用这一个工具;即使是新会话、即使是点「编辑」按钮进来的,也照样先查。
按用户提到的网址、标题及源码选择;in_current_project=true的通常就是目标。
多个候选无法区分时先询问,不能选最近发布的站点。工具报错或返回空不等于没有站点,
要如实说明查询失败,不能自动新建,也不从目录名猜测编号。editable=false是没有源码工程的老站点,改不了,只能征得用户同意后另建新站。 - 核对入口再改:读取目标源码,同时检查
publish_dir/index.html。
项目根目录和sites/<名字>/各有 index.html 时,不能混淆二者。
发布记录与文件不一致时先查明正确页面目录,再显式指定;不要复制到根目录掩盖问题。 - 原地修改:静态站修改实际页面;React 工程修改 source_dir 中的源码,
恢复依赖并重新构建,确认构建成功、产物根 index.html 存在。 - 显式发布:
- 静态站:
publish_site(title='站点名称', site_id='<查询得到的原编号>', src_dir='<实际页面目录>')。 - 构建型:
publish_site(title='站点名称', site_id='<查询得到的原编号>', source_dir='<源码工程目录>', src_dir='<本次构建输出目录>')。
本机源码和产物都必须在真实本地项目目录内,不使用 /myspace 或旧会话临时路径。
- 静态站:
- 验证后交付:核对返回的 site_id 与目标完全一致、URL 保持不变;
有此前版本号时核对版本递增。通过页面读取/浏览工具检查返回的首页实际包含本次修改,
不能仅凭 ok=true 宣称页面已更新。编号变化或首页仍是旧内容时先定位,不继续创建新站。
无法访问时明确说明尚未完成线上页面验证。 - 编辑迭代不做设计三选一。
新建同样必须显式传 src_dir,成功后保留回执中的 site_id 和准确目录供后续修改。
list_sites不可用时,不套用“后端按项目自动定位”的旧规则;读取最新技能并核对能力版本。
在全新对话里改老站点
用户新开一段对话说“帮我改一下之前那个站”时,不需要先切回原会话——list_sites 返回的source_dir 当前就可直接读写(云端是项目文件夹、本机是绑定的本地目录),按这四步走即可:
list_sites()→ 按用户说的网址或标题认出目标,多个拿不准就问;- 去该条的
source_dir原地改(构建型在source_dir改完重新构建); publish_site(title=…, site_id='<查到的编号>', src_dir='<静态站用 publish_dir/构建型用本次产物目录>');- 核对返回的 site_id 与 URL 没变、版本号 +1,再打开线上首页确认改动真的在,才交付。
绝不能因为“这段对话没绑项目”就不带 site_id 直接发布——那会新建一个站点,
用户原来的网址纹丝不动,看起来就是“改了但没更新”。
沙箱生命周期规则(React 工程务必遵守)
沙箱空闲一段时间会被回收销毁,只有项目文件夹(/myspace/...)里的文件永久幸存:
- 源码只能放项目文件夹或尽快通过发布进入项目;
/workspace/其余区域随沙箱销毁丢失。 node_modules/ 构建产物 / 缓存永远不进项目文件夹(工程里的node_modules
是指向/workspace临时区的符号链接,不要删除或替换成真实目录)。- 重开会话后发现依赖目录消失/链接失效 → 重跑 init 脚本即自愈(秒级)。
- 发布或 init 过程中
package.json/package-lock.json落入项目文件夹时可能弹一次
「同步到我的空间」确认,属正常,等用户确认即可。
约束与要点
- 规模上限:≤300 文件、总量 ≤30MB、单文件 ≤10MB(React 产物通常几 MB,余量充足)。
- 公开站点在浏览器里以沙箱模式运行(无 cookie / localStorage)——不要写依赖
登录态或浏览器本地存储的逻辑,持久化用下面的轻后端 API。 - 可见性
visibility:public(默认,凭链接访问)/private(仅本人登录可见)/
站点内置轻后端 API(可选,需要动态能力时用)
站内 JS 用相对路径(不带前导 /)fetch,平台已配好 CORS:
- KV 存储(计数器 / 分数 / 简单配置):
- 读:
GET __api/kv/<key>→{value, exists} - 写:
PUT __api/kv/<key>,body{"value":"..."}(≤4KB,≤200 键)
- 读:
- 表单收集(留言 / 报名 / 反馈,站主可在站点管理里导出 CSV):
POST __api/forms/<form_key>,body 为扁平 JSON 对象(≤8KB)
__api/是保留前缀,站点文件不能用这个目录名。React 工程用src/lib/siteApi.js封装,静态站直接fetch('__api/kv/score')。
你自己读写 KV:用工具,不要 curl __api/
__api/ 那套是给站内 JS(浏览器里)用的。你在沙箱里没有站点会话,直接 curl
它对私密 / 团队站会 404,公开站也不保证连得通——用这四个工具:
site_kv_list(site_id 或 slug)— 看现在有哪些键(默认 50 条,值给前 200 字预览;total比返回条数多就说明还有,调大limit)site_kv_get(key, site_id 或 slug)— 取某个键的全文site_kv_set(key, value, site_id 或 slug)— 写 / 覆盖,改的就是线上数据site_kv_delete(key, site_id 或 slug)— 删键
site_id 取自 list_sites 或 publish_site 回执;slug 是访问地址/site/<slug>/ 中间那段。
KV 是这个站点自带的轻量数据库——页面运行时读写的数据都在这儿。改数据是日常
操作,随时可做,不需要重新发布。
什么时候重新发布,什么时候只改 KV
分界不是"什么时候改",而是改的是代码还是数据:
| 用户要改的 | 走哪条 |
|---|---|
| 版式、结构、交互、新增栏目 / 图表、换图表类型 | 改源码 → publish_site |
| 页面某处显示的值,而该处本来就从 KV 读 | site_kv_set 直接改,立即生效、不用发布 |
| 页面某处的值,但它写死在源码里 | 改源码重发;若用户会反复改这段,先把它改造成从 KV 读(改一次源码发一次),之后每次改数据只动 KV |
| 访客产生的数据(计数 / 投票 / 打分) | 默认别动;用户要清零或纠错时才改 |
所以接到"把 X 改成 Y",先读源码查这个站是怎么做的:X 是写死的,还是kvGet('x') 来的?再决定走哪条路。不要一律当成"改内容就得重新发布"。
同理,用户抱怨"每次改个数字都要重发一次"时,主动提议把这段数据挪进 KV——这是
站点该有的优化,不是额外工作。
规矩:
- 改之前先
site_kv_get看现值——site_kv_set是整值替换,凭印象重写会丢数据; - 结构化内容自己
json.dumps成字符串再传,读回来自己解析; - 删键不可撤销,先跟用户确认键名;
- 公开站的
__api/kv访客也能写(只有限流),KV 里的东西不防篡改——密钥、
不容改动的声明性内容仍写死在源码里; - 上限:单值 ≤4KB、每站 ≤200 键,键名限字母数字和
_.:-。装不下的大块数据不要
硬塞 KV,放进站点文件随发布上线。
禁止事项清单
- 禁止在工程目录(尤其
/myspace/下)直接npm install——加依赖走"改
package.json + 重跑 init 脚本"。 - 禁止把源码目录当站点发布(路径 B 的
src_dir必须指向构建产物目录)。 - 禁止改
base: './'、换 BrowserRouter、引外部 CDN。 - 禁止构建报错未修就发布;禁止编辑会话里绕开原文件另建新站。
- 禁止把设计 mockup 截图 pin 给用户、或对同一问题反复调 choose_design。
交付话术
把返回的 url 以 markdown 链接发给用户,并告知:可在「实验室 → 站点」里管理
(改可见性 / 版本回滚 / 看访问量 / 导出表单数据),点站点卡片上的「编辑」按钮可随时
回来通过对话继续修改。
示例
用户:"做一个部门数据看板网站,能看各科室的月度指标,要好看。"
你(路径 B):init 模板 → 问一句"指标数据我先用示例数据占位,之后您可以发我真实数据"
→ 写 3 个 mockup(深色科技风 / 明亮商务风 / 极简卡片风)截图 → choose_design 等用户
选 → 按选中方案实现(antd 布局 + echarts 图表 + siteApi 存配置)→ npm run build →publish_site(title='部门数据看板', src_dir='/workspace/.site-dist/dashboard', source_dir='/workspace/site-src/dashboard') → 交付链接 + 管理/编辑指引。