在 GitHub 上闲逛时发现了 Innei 的 SKILL 仓库,里面有个 session-to-skill-and-blog 技能,核心思想很妙:把一次折腾的工程会话,沉淀成两个产物——一个可复用的 AI skill,一篇带链接的博客。铁律是先写 skill 再写 blog,因为 blog 需要 skill 的 URL,而且 SKILL.md 的格式强迫先把操作性内容理清楚。想法很好,但它是为 Innei 自己的基础设施定制的——skill 存他的 GitHub 仓库,blog 通过 mxs CLI 发到他的 mx-space 博客。我也有 mx-space(pinw.ca),所以决定把它改造成适配自己环境的版本。这篇文章记录改造过程中踩的坑和学到的教训。第一坑:服务端不在本地安装完 skill 后第一件事就是跑 mxs auth login。结果:TEXT → probing http://localhost:2333… ✘ connection refused 我的 mx-space 跑在远程服务器上,API 地址是 https://api.pinw.ca,不是本机。尝试加 --api-url 参数:BASH mxs --api-url https://api.pinw.ca auth login 没用,它还是探 localhost。翻源码发现 mxs auth login 的 --api-url 传递链路有问题——global flag 在 auth 子命令里没有正确接管。换用环境变量:BASH MXS_API_URL=https://api.pinw.ca pnpm mxs auth login 这次成功了。认证后 config 写入了 ~/.config/mxs/profiles/default/config.json。Note教训:mx-space CLI 的 global flag 和子命令之间的 override 优先级在不同版本中行为不一致。环境变量 MXS_API_URL 是最可靠的跨版本配置方式。第二坑:profile 不自动激活认证成功后,我以为万事大吉。直接跑:BASH pnpm mxs auth whoami # ✘ not authenticated 但 auth status 却正常:BASH pnpm mxs auth status # ● signed in — pintaste · pintaste@outlook.com 再看 mxs category list:TEXT ✘ connection refused 它又在连 localhost。明明 config.json 里 api_url 已经是 https://api.pinw.ca,current 指针也指向 default。但 pnpm mxs 在 monorepo 上下文中没有正确读取 profile。显式指定 profile 解决:BASH pnpm mxs --profile default category list --output llm # ✅ 返回 9 个分类 于是我在 resolve-mxs.sh 里加上了 --profile default,所有脚本调用 mxs 时自动带上。Note教训:在 monorepo 里通过 pnpm mxs 调用时,profile 解析路径可能不同于全局安装。显式 --profile 比依赖 current 指针更可靠。第三坑:snippet API 版本差异原版 skill 的 push-skill.sh 使用:BASH mxs snippet create --name "skill-name" --type skill --file SKILL.md mxs snippet get "skill/skill-name" --json 但我的 mxs 版本(0.13.2)的 snippet 子命令完全不同——它是 VFS 风格的:BASH mxs snippet put --type skill --file SKILL.md "skills/name/SKILL.md" 而且路径必须以 /SKILL.md 结尾:TEXT ✘ skill snippet path must end with /SKILL.md 这个约束在文档里没有任何提示,只能从错误信息反推。适配后的 push-skill.sh 从 60 行精简到 30 行,因为 VFS API 本身就是幂等的——同一个 path 重复 put 就是更新。第四坑:过早抽象第一版改造我犯了一个经典错误:看到用户的博客目录下有 hugo.pinw.ca/,就假设博客是 Hugo,把整个 skill 改成了 Hugo 工作流(hugo new、git push → Netlify)。用户纠正说「我现在的网站也是 mx-space」——原来 pinw.ca/core/ 和 pinw.ca/Yohaku/ 才是主站。这个错误的根因是:在没确认当前架构的情况下,基于目录名做了推断。 hugo.pinw.ca 是旧博客的遗留目录,真正在跑的 mx-space 后端就在旁边的 pinw.ca/core/。Warning教训:不要根据目录名推断技术栈。先问用户,或者检查实际运行的服务。一个目录叫 hugo.xxx 不代表 Hugo 是主站。最终的改造方案以下是适配前后的对比:组件Innei 原版pinw.ca 版本Skill 存储~/git/innei-repo/skill/~/.pi/agent/skills/mxs 调用全局 mxspnpm mxs --profile default脚手架带 README/symlink/git stage只创建目录 + stubSnippet APIcreate --type skillput --type skill (VFS)配置~/.config/innei-skills/config.json无需配置文件全部 7 个脚本中,5 个需要改写(resolve-skill-repo.sh、scaffold-skill.sh、push-skill.sh、publish-post.sh、get-post.sh、update-post.sh),1 个新增(resolve-mxs.sh),1 个保持不变(load-litexml.sh)。可带走的判断这次改造暴露了三个通用原则,不只是 mx-space 适用:确认运行态,不要推断。目录名叫 hugo.xxx 不代表主站是 Hugo。先问 ps、docker ps、curl,再动手。CLI 工具的 global flag 不可靠时,用环境变量。--api-url 在子命令中的传递行为因版本而异,MXS_API_URL 在源码里是第一优先级。Monorepo 里的 CLI 调用需要显式 profile。pnpm mxs 的上下文解析不同于全局 mxs,--profile 是唯一可靠的定位方式。改造完成后的 skill 已经在 ~/.pi/agent/skills/session-to-skill-and-blog/ 就位,本文正是用它发布的——狗粮自食。Skill 地址:Innei/SKILL — session-to-skill-and-blog(原版,本文的灵感来源)
在 GitHub 上闲逛时发现了 Innei 的 SKILL 仓库,里面有个 session-to-skill-and-blog 技能,核心思想很妙:把一次折腾的工程会话,沉淀成两个产物——一个可复用的 AI skill,一篇带链接的博客。铁律是先写 skill 再写 blog,因为 blog 需要 skill 的 URL,而且 SKILL.md 的格式强迫先把操作性内容理清楚。
想法很好,但它是为 Innei 自己的基础设施定制的——skill 存他的 GitHub 仓库,blog 通过 mxs CLI 发到他的 mx-space 博客。我也有 mx-space(pinw.ca),所以决定把它改造成适配自己环境的版本。这篇文章记录改造过程中踩的坑和学到的教训。
第一坑:服务端不在本地
安装完 skill 后第一件事就是跑 mxs auth login。结果:
我的 mx-space 跑在远程服务器上,API 地址是 https://api.pinw.ca,不是本机。尝试加 --api-url 参数:
没用,它还是探 localhost。翻源码发现 mxs auth login 的 --api-url 传递链路有问题——global flag 在 auth 子命令里没有正确接管。换用环境变量:
这次成功了。认证后 config 写入了 ~/.config/mxs/profiles/default/config.json。
教训:mx-space CLI 的 global flag 和子命令之间的 override 优先级在不同版本中行为不一致。环境变量 MXS_API_URL 是最可靠的跨版本配置方式。
第二坑:profile 不自动激活
认证成功后,我以为万事大吉。直接跑:
但 auth status 却正常:
再看 mxs category list:
它又在连 localhost。明明 config.json 里 api_url 已经是 https://api.pinw.ca,current 指针也指向 default。但 pnpm mxs 在 monorepo 上下文中没有正确读取 profile。
显式指定 profile 解决:
于是我在 resolve-mxs.sh 里加上了 --profile default,所有脚本调用 mxs 时自动带上。
教训:在 monorepo 里通过 pnpm mxs 调用时,profile 解析路径可能不同于全局安装。显式 --profile 比依赖 current 指针更可靠。
第三坑:snippet API 版本差异
原版 skill 的 push-skill.sh 使用:
但我的 mxs 版本(0.13.2)的 snippet 子命令完全不同——它是 VFS 风格的:
而且路径必须以 /SKILL.md 结尾:
这个约束在文档里没有任何提示,只能从错误信息反推。适配后的 push-skill.sh 从 60 行精简到 30 行,因为 VFS API 本身就是幂等的——同一个 path 重复 put 就是更新。
第四坑:过早抽象
第一版改造我犯了一个经典错误:看到用户的博客目录下有 hugo.pinw.ca/,就假设博客是 Hugo,把整个 skill 改成了 Hugo 工作流(hugo new、git push → Netlify)。用户纠正说「我现在的网站也是 mx-space」——原来 pinw.ca/core/ 和 pinw.ca/Yohaku/ 才是主站。
这个错误的根因是:在没确认当前架构的情况下,基于目录名做了推断。 hugo.pinw.ca 是旧博客的遗留目录,真正在跑的 mx-space 后端就在旁边的 pinw.ca/core/。
教训:不要根据目录名推断技术栈。先问用户,或者检查实际运行的服务。一个目录叫 hugo.xxx 不代表 Hugo 是主站。
最终的改造方案
以下是适配前后的对比:
全部 7 个脚本中,5 个需要改写(resolve-skill-repo.sh、scaffold-skill.sh、push-skill.sh、publish-post.sh、get-post.sh、update-post.sh),1 个新增(resolve-mxs.sh),1 个保持不变(load-litexml.sh)。
可带走的判断
这次改造暴露了三个通用原则,不只是 mx-space 适用:
改造完成后的 skill 已经在 ~/.pi/agent/skills/session-to-skill-and-blog/ 就位,本文正是用它发布的——狗粮自食。
Skill 地址:Innei/SKILL — session-to-skill-and-blog(原版,本文的灵感来源)