涉及源码 :
src/reference_data/commands_snapshot.json、tools_snapshot.json,src/commands.py、src/tools.py、src/execution_registry.py、src/runtime.py、src/main.py,src/models.py(PortingModule)。
1. 「两个宇宙」是什么
在本仓库里,命令宇宙 与工具宇宙 是两套有穷、可版本化 的镜像清单,都从 JSON 快照加载为内存中的 PortingModule 元组:
- 命令 :
src/reference_data/commands_snapshot.json→PORTED_COMMANDS(commands.py) - 工具 :
src/reference_data/tools_snapshot.json→PORTED_TOOLS(tools.py)
快照里每条记录至少包含 name、source_hint、responsibility (加载时映射进 PortingModule,status='mirrored'):
python
# 23:33:src/commands.py
def load_command_snapshot() -> tuple[PortingModule, ...]:
raw_entries = json.loads(SNAPSHOT_PATH.read_text())
return tuple(
PortingModule(
name=entry['name'],
responsibility=entry['responsibility'],
source_hint=entry['source_hint'],
status='mirrored',
)
for entry in raw_entries
)
python
# 23:34:src/tools.py
def load_tool_snapshot() -> tuple[PortingModule, ...]:
raw_entries = json.loads(SNAPSHOT_PATH.read_text())
return tuple(
PortingModule(
name=entry['name'],
responsibility=entry['responsibility'],
source_hint=entry['source_hint'],
status='mirrored',
)
for entry in raw_entries
)
产品语义上 (与具体 IDE/Agent 产品对齐时):命令 多对应用户显式 /slash、调色板动作、工作流入口;工具 多对应模型 function-calling / MCP 暴露给模型的可调用面。claw-code 在 路由层用 kind: command | tool 把两宇宙拆开评分与排序,在 执行层 用不同 shim 签名(prompt vs payload)区分。
2. 清单如何「驱动」------三层流水线
可以把数据流画成:快照 → 全量宇宙 →(可选过滤视图)→ 路由 → 注册表 shim → QueryEnginePort 摘要。
| 阶段 | 命令侧 | 工具侧 |
|---|---|---|
| 真相源 | commands_snapshot.json |
tools_snapshot.json |
| 全量常量 | PORTED_COMMANDS |
PORTED_TOOLS |
| 过滤视图 | get_commands(..., include_plugin_commands, include_skill_commands) |
get_tools(simple_mode, include_mcp, permission_context) |
| 路由输入 | route_prompt 用 全量 PORTED_COMMANDS |
同上 PORTED_TOOLS |
| 精确执行 | execute_command / MirroredCommand |
execute_tool / MirroredTool |
关键点 :路由不看 get_tools 的权限过滤 。即使用户 CLI 里 --deny-prefix mcp 缩小了「可见工具列表」,默认 route_prompt 仍会在完整 PORTED_TOOLS 上匹配 (与「模型实际能调用什么」的严格对齐需在上层再收敛,见 result/05.md)。
3. 路由:PortRuntime.route_prompt
3.1 分轨收集 + 公平首发 + 余量排序
python
# 90:107:src/runtime.py
def route_prompt(self, prompt: str, limit: int = 5) -> list[RoutedMatch]:
tokens = {token.lower() for token in prompt.replace('/', ' ').replace('-', ' ').split() if token}
by_kind = {
'command': self._collect_matches(tokens, PORTED_COMMANDS, 'command'),
'tool': self._collect_matches(tokens, PORTED_TOOLS, 'tool'),
}
selected: list[RoutedMatch] = []
for kind in ('command', 'tool'):
if by_kind[kind]:
selected.append(by_kind[kind].pop(0))
leftovers = sorted(
[match for matches in by_kind.values() for match in matches],
key=lambda item: (-item.score, item.kind, item.name),
)
selected.extend(leftovers[: max(0, limit - len(selected))])
return selected[:limit]
工程含义:
- 分词 :把
/、-替换成空格再split,得到小写 token 集合(适合从自然语言 prompt 里「捞」到与name/source_hint重叠的词)。 - 双宇宙并行评分 :对命令、工具各跑一遍
_collect_matches,得到两个已按分数排序的列表。 - 各取第一名 :先从 command 榜、tool 榜 各拿一个最高分 (若存在),避免某一宇宙霸占整个
limit。 - 合并余量 :剩余候选按 分数降序 ,同分按
kind、name稳定排序,填满limit。
3.2 单条评分:名字 + 路径提示 + 职责描述
python
# 176:192:src/runtime.py
def _collect_matches(self, tokens: set[str], modules: tuple[PortingModule, ...], kind: str) -> list[RoutedMatch]:
matches: list[RoutedMatch] = []
for module in modules:
score = self._score(tokens, module)
if score > 0:
matches.append(RoutedMatch(kind=kind, name=module.name, source_hint=module.source_hint, score=score))
matches.sort(key=lambda item: (-item.score, item.name))
return matches
@staticmethod
def _score(tokens: set[str], module: PortingModule) -> int:
haystacks = [module.name.lower(), module.source_hint.lower(), module.responsibility.lower()]
score = 0
for token in tokens:
if any(token in haystack for haystack in haystacks):
score += 1
return score
学习点 :路由是 子串命中计数 ,不是 embedding;source_hint 含文件路径 ,用户说 MCP、review 等词时容易同时命中命令与工具条目,正好检验 双轨公平 逻辑。
RoutedMatch 携带 kind ,后续执行与 QueryEnginePort 的 matched_commands / matched_tools 分拆都依赖它:
python
# 16:21:src/runtime.py
@dataclass(frozen=True)
class RoutedMatch:
kind: str
name: str
source_hint: str
score: int
4. Shim 执行:ExecutionRegistry → execute_*
4.1 注册表 = 全量清单的薄包装
python
# 47:51:src/execution_registry.py
def build_execution_registry() -> ExecutionRegistry:
return ExecutionRegistry(
commands=tuple(MirroredCommand(module.name, module.source_hint) for module in PORTED_COMMANDS),
tools=tuple(MirroredTool(module.name, module.source_hint) for module in PORTED_TOOLS),
)
registry.command(name) / registry.tool(name) 大小写不敏感 查找,与 get_command / get_tool 一致。
4.2 命令 shim:prompt 字符串
python
# 75:80:src/commands.py
def execute_command(name: str, prompt: str = '') -> CommandExecution:
module = get_command(name)
if module is None:
return CommandExecution(name=name, source_hint='', prompt=prompt, handled=False, message=f'Unknown mirrored command: {name}')
action = f"Mirrored command '{module.name}' from {module.source_hint} would handle prompt {prompt!r}."
return CommandExecution(name=module.name, source_hint=module.source_hint, prompt=prompt, handled=True, message=action)
4.3 工具 shim:payload 字符串
python
# 81:86:src/tools.py
def execute_tool(name: str, payload: str = '') -> ToolExecution:
module = get_tool(name)
if module is None:
return ToolExecution(name=name, source_hint='', payload=payload, handled=False, message=f'Unknown mirrored tool: {name}')
action = f"Mirrored tool '{module.name}' from {module.source_hint} would handle payload {payload!r}."
return ToolExecution(name=module.name, source_hint=module.source_hint, payload=payload, handled=True, message=action)
命名差异 是接口层对未来 「命令吃自然语言 / 工具吃 JSON payload」 的预留;当前 bootstrap 里工具也传了用户 prompt 当 payload:
python
# 119:120:src/runtime.py
command_execs = tuple(registry.command(match.name).execute(prompt) for match in matches if match.kind == 'command' and registry.command(match.name))
tool_execs = tuple(registry.tool(match.name).execute(prompt) for match in matches if match.kind == 'tool' and registry.tool(match.name))
即:路由后的统一「用户 utterance」 先灌进两条执行链,真实现时可再分支解析。
5. bootstrap_session:路由 + shim + 引擎一条链
python
# 117:133:src/runtime.py
matches = self.route_prompt(prompt, limit=limit)
registry = build_execution_registry()
command_execs = tuple(registry.command(match.name).execute(prompt) for match in matches if match.kind == 'command' and registry.command(match.name))
tool_execs = tuple(registry.tool(match.name).execute(prompt) for match in matches if match.kind == 'tool' and registry.tool(match.name))
denials = tuple(self._infer_permission_denials(matches))
stream_events = tuple(engine.stream_submit_message(
prompt,
matched_commands=tuple(match.name for match in matches if match.kind == 'command'),
matched_tools=tuple(match.name for match in matches if match.kind == 'tool'),
denied_tools=denials,
))
turn_result = engine.submit_message(
prompt,
matched_commands=tuple(match.name for match in matches if match.kind == 'command'),
matched_tools=tuple(match.name for match in matches if match.kind == 'tool'),
denied_tools=denials,
)
顺序 :matches → 按 kind 跑 shim → 推断 denials → 把 名称元组 (不是路径)交给 QueryEnginePort。
报告 :RuntimeSession.as_markdown() 分别打印 Routed Matches、Command Execution、Tool Execution、Stream Events、Turn Result (见 runtime.py 中 as_markdown),便于肉眼核对 「路由到了谁 → shim 说了啥 → 引擎摘要里写了啥」。
6. CLI:清单自省、路由调试、按名 shim
| 子命令 | 作用 |
|---|---|
commands / tools |
走 get_commands / get_tools(可带过滤),列表视图 |
route <prompt> |
直接 route_prompt,打印 kind\tname\tscore\tsource_hint |
show-command / show-tool |
get_command / get_tool 按精确名 看单条条目 |
exec-command / exec-tool |
不经路由,按名执行 shim(失败 exit 1) |
python
# 142:148:src/main.py
if args.command == 'route':
matches = PortRuntime().route_prompt(args.prompt, limit=args.limit)
if not matches:
print('No mirrored command/tool matches found.')
return 0
for match in matches:
print(f'{match.kind}\t{match.name}\t{match.score}\t{match.source_hint}')
python
# 200:207:src/main.py
if args.command == 'exec-command':
result = execute_command(args.name, args.prompt)
print(result.message)
return 0 if result.handled else 1
if args.command == 'exec-tool':
result = execute_tool(args.name, args.payload)
print(result.message)
return 0 if result.handled else 1
测试锚点 :test_route_and_show_entry_cli_run、test_exec_command_and_tool_cli_run、test_execution_registry_runs 等把 快照非空、路由可命中、shim 有固定子串 锁在 CI 里。
7. 镜像数据的「真实形状」与陷阱
7.1 同名多条目
快照里 同一 name 可对应多条 (例如多个 source_hint 指向不同文件):
json
{ "name": "add-dir", "source_hint": "commands/add-dir/add-dir.tsx", ... },
{ "name": "add-dir", "source_hint": "commands/add-dir/index.ts", ... },
get_command / get_tool 线性扫描 ,返回 首个 大小写匹配项:
python
# 52:56:src/commands.py
def get_command(name: str) -> PortingModule | None:
needle = name.lower()
for module in PORTED_COMMANDS:
if module.name.lower() == needle:
return module
后果 :shim 与 show-command 展示的 source_hint 可能只是其中一条;路由里 每条快照行仍是独立 PortingModule ,同名可多次命中、多次出现在不同 RoutedMatch(若分数与 limit 允许)。产品化时常需要 primary_fqn 或 entry_id 消歧。
7.2 工具快照里的「非顶层工具」名
tools_snapshot.json 中会出现 UI、agentMemory 等与「用户心智中的工具类名」混在一起的条目,它们来自归档路径镜像。路由 仍按 token 子串匹配------这是 移植期忠实还原表面 ,不是最终 UX;成熟产品会在 清单层 再标 callable: bool 或 分层 tool spec。
8. 小结
- 命令宇宙 / 工具宇宙 由 JSON 快照 →
PORTED_*→(可选)get_*过滤 构成;路由固定吃全量PORTED_*,与列表过滤解耦。 - 路由 在两条宇宙上 独立打分、各取榜首、再合并余量 ,输出带
kind的RoutedMatch。 - Shim 经
ExecutionRegistry统一按名查找;命令 用prompt语义,工具 用payload语义,bootstrap 阶段暂用同一用户字符串灌入。 - CLI 提供 列表 / 路由 / 精确查看 / 精确执行 四条路径,支撑移植期 可测试、可调试 闭环。