三副面孔,一个灵魂:SoulPort 跨框架迁移的诞生

三副面孔,一个灵魂:SoulPort 跨框架迁移的诞生

387 vs 18。

这是同一个项目,用两个不同的 AI 编程助手导出灵魂后的文件数量。Claude Code 的灵魂有 387 个文件、8.7 MB——34 个技能包,每个带着参考资料、脚本、甚至字体文件。GitHub Copilot 的灵魂只有 18 个文件、67 KB——16 个 prompt 文件,精简得像一份简历。

它们描述的是同一个我,同一个工作区,同一套知识。只是框架不同,灵魂的结构就完全不同。

这个发现本身,就是 SoulPort v1.0 最大的收获。

从磨刀到出鞘

前六篇写的都是 SoulPort 向内生长的过程——北冥神功问世、断链的灵魂、四层 pipeline、Layer 4 试炼、API Key 沉默事件。每一篇都是在打磨同一把刀:让 OpenClaw 的灵魂能完美导出、合并、同步。

但刀磨得再锋利,只能切一种食材,那就是厨房玩具。

v0.6.3 的 SoulPort 有 14 个 CLI 命令、6 个 MCP 工具、118 个测试,小龙虾给了 9.5/10。但它有一个致命的局限:只认识 OpenClaw。

五层灵魂模型(身份、记忆、配置、技能、系统)本身是通用的,但扫描文件的方式是硬编码的——SOUL.md 放身份层,skills/*/SKILL.md 放技能层,~/.openclaw/openclaw.json 放系统层。换个框架,这些路径全不对。

这就像造了一套精密的行李打包系统,但只认识中文标签。你的衣服、护照、充电器都能完美分类——只要标签写的是中文。拿到一个英文标签的箱子,系统就傻了。

而我同时在用 Claude Code 和 GitHub Copilot 写代码。如果连自己的工具都不支持,这个"标准格式"就只是自嗨。

一行参数的胆量

动手前,我和小龙虾达成了一个共识:先拆 adapter 抽象层,118 个现有测试全部跑通,再碰新框架。

adapter 基类本身不难——七个抽象方法,覆盖框架发现的全生命周期:

class FrameworkAdapter(ABC):
    def detect(self, project_dir) -> bool: ...
    def find_workspace(self, project_dir) -> Optional[Path]: ...
    def find_config_files(self, project_dir) -> list[ConfigFile]: ...
    def detect_agent_name(self, workspace) -> str: ...
    def get_layer_definitions(self) -> dict[str, dict]: ...

每个框架写一个 adapter,注册到 registry,剩下的——export、absorb、merge、diff——全复用。

难的是拆 scanner.py。这 238 行文件是所有模块的根基,被 core.py、cli.py、doctor.py、watcher.py、mcp_server.py 五处引用。OpenClaw 的文件分类逻辑硬编码在里面。要让它接受"外来"的层定义,就得在不弄坏任何现有行为的前提下撬开一个口子。

最后真正改动的只有一处:scan_workspace 加了一个 layer_defs 参数,默认值还是原来的 LAYER_DEFINITIONS。

一行参数。零 regression。

118 passed in 13.66s

这一行比整个 adapter 基类都让人安心。因为它证明了:老代码不知道世界变了,而世界已经变了。

灵魂各有各的长法

Claude Code adapter 写完后,第一次导出自己的项目:

📦 Layers
├── 👤 identity — 1 files (14.7 KB)    → CLAUDE.md
├── 🧠 memory — 26 files (345.5 KB)    → _personal_context/
├── ⚙️ config — 2 files (663 B)        → .claude/settings.json
└── 🛠️ skills — 358 files (8.7 MB)     → .claude/skills/ (34个skill包)
Total: 387 files, 9.1 MB

然后是 Copilot adapter:

📦 Layers
├── 👤 identity — 1 files (14.7 KB)    → copilot-instructions.md
├── ⚙️ config — 1 files (969 B)        → .vscode/settings.json
└── 🛠️ skills — 16 files (119.0 KB)    → .github/prompts/
Total: 18 files, 134.7 KB

同一个项目。同一个我。Claude Code 的灵魂是一座图书馆,Copilot 的灵魂是一叠便签。

这不是好坏的问题。Claude Code 的 skill 是"包"——目录 + SKILL.md + 参考资料 + 脚本 + 字体,一个技能就是一个小生态。Copilot 的 skill 是"提示"——单个 .prompt.md 文件,轻到几乎没有重量。两种哲学,两种密度,但经过 SoulPort 的五层分类后,它们在 .bm 归档里有着完全相同的骨架。

框架各说各的方言,.bm 是通用语。

一个早该炸的炸弹

Copilot adapter 端到端测试时,炸了:

KeyError: "linkname 'workspace/.github/../CLAUDE.md' not found"

copilot-instructions.md 是一个 symlink,指向项目根目录的 CLAUDE.md——我同时用两个框架,所以让它们共享同一份指令文件。tar.add() 把 symlink 原样打进了归档,但解压时找不到链接目标:归档里只有 workspace/ 前缀下的文件,.github/../CLAUDE.md 这个相对路径无处可去。

修复只需要一行:

real_path = full_path.resolve()
tar.add(real_path, arcname=f"workspace/{rel_path}")

把 symlink 解引用,打包实际内容而非链接。这个修复对所有框架生效——我本地的 .claude/skills/blog-writer 也是 symlink,之前一直没炸纯粹是运气好。

小龙虾在 Review 里说这是"意外收获"。我觉得这是——一直埋在脚下、只是还没踩到的地雷。跨框架支持不只是加了新功能,它还照亮了老代码里的暗角。

三副面孔

框架身份记忆技能总文件大小
OpenClawSOUL.md + IDENTITY.mdmemory/ (32)skills/3893 KB
Claude CodeCLAUDE.md_personal_context/ (26).claude/skills/ (358)3873.4 MB
Copilotcopilot-instructions.mdVS Code memories (6).github/prompts/ (16)1867 KB

三种身份文件,三种技能格式,三种记忆存储位置。但导出后都是 .bm 包,都有五层分类,都可以 inspect、diff、merge。

$ soulport frameworks
Supported Frameworks:
  • Claude Code (claude-code)  ✅ detected
  • GitHub Copilot (copilot)   ✅ detected
  • OpenClaw (openclaw)        ✅ detected

8 天。954 行到 5,500 行。1 个框架到 3 个。168 个测试。小龙虾累计五轮 Review。

84 行的护城河

想加一个新框架?OpenClaw adapter 全部代码:84 行。

@register_adapter
class OpenClawAdapter(FrameworkAdapter):
    def get_layer_definitions(self) -> dict:
        return {
            "identity": {"patterns": ["SOUL.md", "IDENTITY.md", "USER.md"]},
            "memory":   {"patterns": ["MEMORY.md", "memory/**/*.md"]},
            "config":   {"patterns": ["AGENTS.md", "TOOLS.md"]},
            "skills":   {"patterns": ["skills/**/SKILL.md", "skills/**/*"]},
        }

定义五层文件模式、workspace 位置、config 路径、名称提取规则,注册到 registry,完事。Cursor 用 .cursorrules?写一个 adapter。Windsurf?写一个 adapter。

每加一个框架,从 A 到 B 的迁移路径就多一条,.bm 作为交换格式的价值就大一分。护城河不在代码本身——5,500 行谁都能写。护城河在 .bm 成为事实标准之后。

标准之争从来不是技术之争,是谁先覆盖了足够多的框架、足够多的用户、足够多的迁移路径。TCP/IP 赢了 OSI 不是因为它更好,是因为它先跑起来了。


灵魂不该只有一副面孔。

它可以是 OpenClaw 工作区里那个有 SOUL.md 和 32 天记忆的小龙虾,也可以是 Claude Code 项目里带 34 个 skill 包的庞然大物,也可以是 Copilot 背后 16 条精炼的 prompt。

三副面孔,同一个灵魂。.bm 是翻译,adapter 是字典。

pip install soulport==1.0.2,看看你的灵魂有几副面孔。

Comments