- 人工智能
- AI 应用
- 提示工程
- 开发工具
- 工作流自动化
- AI Agent
【免费下载链接】get-shit-done
A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.
本文对应仓库 changeset
[.changeset/brave-mice-build.md](https://link.gitcode.com/i/af525bafaffcfc4329a43f0afdeee179)(PR #3069,类型Changed)。它记录了 get-shit-done SDK 查询层的一次关键结构收敛:查询命令元数据统一经由一个规范化的 Command Definition Module 接口(family/canonical/aliases/mutation/output_mode/handler_key)流转,使注册表装配、变更语义与别名生成共用同一数据源,从而提升局部性(locality)并减少漂移(drift);同时清理了 CJS 回退路径的错误映射,将原始err原样传给mapFallbackDispatchError。读完本文,你将掌握该模块的接口定义、命令清单(manifest)来源、派生视图、注册表装配顺序,以及 native / CJS / error 三种派发模式下的结构化错误分类机制。
背景:查询层为何需要一个"命令定义"单一来源
get-shit-done 的 SDK 查询层(sdk/src/query/)承担着gsd-sdk queryCLI 与createRegistry()程序化调用两种消费方式。历史上,命令的规范名(canonical)、别名(alias)、变更语义(mutation)、输出模式(output_mode)、处理器绑定(handler_key)分散在多个模块中各自推导,容易造成同一命令在不同模块中产生不一致的元数据(即"漂移",drift)。
[CONTEXT.md](https://link.gitcode.com/i/744604702fe4539f1baa3e5b6a7418d7)将 Command Definition Module 明确定义为:
Canonical command metadata Interface powering alias, catalog, and semantics generation.
即:命令元数据的规范化接口,为别名生成、目录生成与语义生成提供动力。changeset 中描述的收敛目标正是让"注册表装配(registry assembly)、变更语义(mutation semantics)、别名生成(alias generation)"三者消费同一个接口,这是[docs/adr/0001-dispatch-policy-module.md](https://link.gitcode.com/i/a4c333644ccfd34be384c52736fa7b9f)所确立的"政策深度集中、适配器保持轻薄"设计方向的延续。
CommandDefinition 接口:六个字段的语义
核心接口定义于[sdk/src/query/command-definition.ts](https://link.gitcode.com/i/11e539645ee3dd0835ac9cef3daeb721):
export interface CommandDefinition { family?: CommandFamily; canonical: string; aliases: string[]; mutation: boolean; output_mode: OutputMode; handler_key?: string; }各字段含义如下:
| 字段 | 类型 | 含义 |
|---|---|---|
family | CommandFamily(可选) | 命令所属家族,取值state/verify/init/phase/phases/validate/roadmap(见[sdk/src/query/command-manifest.types.ts](https://link.gitcode.com/i/20d9547d3393dfcdd00908a980f9bf8d))。非家族命令没有该字段 |
canonical | string | 规范命令名,如state.load、milestone.complete;家族命令的 canonical 必然包含. |
aliases | string[] | 别名数组,多为空格分隔形式,如['state json']、['milestone complete'] |
mutation | boolean | 是否产生持久化写入;true表示该命令会修改磁盘状态 |
output_mode | OutputMode | 输出模式,取值json或raw |
handler_key | string(可选) | 处理器注册键,默认回退到canonical |
OutputMode与CommandFamily在[sdk/src/query/command-manifest.types.ts](https://link.gitcode.com/i/20d9547d3393dfcdd00908a980f9bf8d-L3)中定义为'json' | 'raw'与七个家族名。这个接口是"派生视图"的唯一输入源,任何消费方都不得自行维护第二份命令元数据。
命令清单(Manifest)来源:家族清单 + 非家族清单
CommandDefinition 由两份静态清单投影而成,见[sdk/src/query/command-definition.ts](https://link.gitcode.com/i/ffa879be7e5a898b099fbb74c1f36505):
COMMAND_MANIFEST:七个家族的命令清单合并(state、verify、init、phase、phases、validate、roadmap),定义于[sdk/src/query/command-manifest.ts](https://link.gitcode.com/i/1bd7f4e8ce72ec80e4650ce88e17e529);每个家族各自有一个command-manifest.<family>.ts文件(如[command-manifest.state.ts](https://link.gitcode.com/i/097c4cf27ee3414c8618be43339c1691)、command-manifest.roadmap.ts等),成为该家族接缝(seam)的数据源。NON_FAMILY_COMMAND_MANIFEST:非家族命令清单,见[sdk/src/query/command-manifest.non-family.ts](https://link.gitcode.com/i/0f11ec875431255520ddb926c5e996c7),覆盖frontmatter.*、commit、config-*、template.*、milestone.complete、workstream.*、learnings.*、stats.json、verify-summary、agent.classify-failure等。
两处清单的条目类型略有差异:家族条目带family与可选handlerKey(默认等于 canonical),非家族条目没有family。投影规则([command-definition.ts](https://link.gitcode.com/i/dbb39c7a957562721f55f840242c8378))为:
const FAMILY_COMMAND_DEFINITIONS = COMMAND_MANIFEST.map((entry) => ({ family: entry.family, canonical: entry.canonical, aliases: [...entry.aliases], mutation: entry.mutation, output_mode: entry.outputMode, handler_key: entry.handlerKey ?? entry.canonical, }));其中handler_key的默认回退语义(entry.handlerKey ?? entry.canonical)被[command-definition.test.ts](https://link.gitcode.com/i/7360d14eb1a6426ed3200a6c699d6c28)以"handler_key 规范化契约"测试锁定。
以一个真实条目为例([command-manifest.state.ts](https://link.gitcode.com/i/097c4cf27ee3414c8618be43339c1691#L13)):
{ family: 'state', canonical: 'state.update', aliases: ['state update'], mutation: true, outputMode: 'json' }它投影出的 CommandDefinition 即:family: 'state'、canonical: 'state.update'、aliases: ['state update']、mutation: true、output_mode: 'json'、handler_key: 'state.update'。
派生视图:供政策消费方使用的索引
[sdk/src/query/command-definition.ts](https://link.gitcode.com/i/0c4153ee37ddfded4ad562671952c201)在COMMAND_DEFINITIONS基础上导出了一系列只读派生视图,作为政策消费方的统一入口:
| 导出 | 形式 | 用途 |
|---|---|---|
COMMAND_DEFINITIONS | readonly CommandDefinition[] | 家族 + 非家族全部定义 |
COMMAND_DEFINITIONS_BY_FAMILY | Record<CommandFamily, readonly CommandDefinition[]> | 按家族索引 |
COMMAND_DEFINITION_BY_CANONICAL | Record<string, CommandDefinition> | 按 canonical 索引 |
COMMAND_MUTATION_SET | ReadonlySet<string> | 所有会写入磁盘的命令(canonical + aliases) |
COMMAND_RAW_OUTPUT_SET | ReadonlySet<string> | 所有output_mode === 'raw'的命令(canonical + aliases) |
FAMILY_MUTATION_COMMANDS/FAMILY_RAW_OUTPUT_COMMANDS | string[] | 仅家族命令的对应子集 |
QUERY_MUTATION_COMMANDS_FROM_DEFINITIONS/TRANSPORT_RAW_COMMANDS_FROM_DEFINITIONS | string[] | 数组形式的导出,供 CJS 与传输层消费 |
这些视图的关键价值在于"单一来源派生":变更语义与输出模式不再由各调用方各自判断,而是从同一份COMMAND_DEFINITIONS过滤得到。[command-definition.test.ts](https://link.gitcode.com/i/bdd0fa3deea93ad9844adad6ed734a29)为此提供了三重保障:
- 家族索引与定义同步:
COMMAND_DEFINITIONS_BY_FAMILY展开后的 canonical 集合必须与COMMAND_MANIFEST完全一致; - 变更别名单一来源:
FAMILY_MUTATION_COMMANDS同时包含state.update、phase complete、roadmap.update-plan-progress等点号与空格形式; - 索引视图正确性:
COMMAND_MUTATION_SET.has('state.update') === true、has('state.json') === false、COMMAND_RAW_OUTPUT_SET.has('commit') === true。
注册表装配:静态目录 + 别名分组的有序计划
Command Definition Module 直接驱动QueryRegistry的装配。装配计划定义于[sdk/src/query/registry-assembly-descriptor.ts](https://link.gitcode.com/i/a9dcd77b3aca2d032b40554681f19c02):
export const REGISTRY_ASSEMBLY_PLAN: readonly RegistryAssemblyStep[] = [ { kind: 'static', key: 'FOUNDATION_STATIC_CATALOG' }, { kind: 'alias', key: 'state' }, { kind: 'static', key: 'STATE_SUPPORT_STATIC_CATALOG' }, { kind: 'alias', key: 'roadmap' }, { kind: 'static', key: 'MUTATION_SURFACES_STATIC_CATALOG' }, { kind: 'alias', key: 'verify' }, { kind: 'static', key: 'VERIFY_DECISION_STATIC_CATALOG' }, { kind: 'alias', key: 'validate' }, { kind: 'static', key: 'DECISION_ROUTING_STATIC_CATALOG' }, { kind: 'alias', key: 'phase' }, { kind: 'alias', key: 'phases' }, { kind: 'alias', key: 'init' }, { kind: 'static', key: 'DOMAIN_STATIC_CATALOG' }, ] as const;装配步骤分两种:
static步骤:引用STATIC_CATALOG_GROUPS([registry-assembly-descriptor.ts](https://link.gitcode.com/i/17123d157789da5b5305f35fa9a2e588))中的六个静态目录(FOUNDATION_STATIC_CATALOG、STATE_SUPPORT_STATIC_CATALOG、MUTATION_SURFACES_STATIC_CATALOG、VERIFY_DECISION_STATIC_CATALOG、DECISION_ROUTING_STATIC_CATALOG、DOMAIN_STATIC_CATALOG)。alias步骤:对每个家族调用buildAliasGroup(family)([registry-assembly-descriptor.ts](https://link.gitcode.com/i/694746aad69dd126852286e030525b53)),其核心逻辑是从COMMAND_DEFINITIONS_BY_FAMILY取定义、从FAMILY_HANDLERS取处理器,并用entry.handler_key完成绑定:
function buildAliasGroup(family: CommandFamily): RegistryAssemblyAliasGroup { const definitions = COMMAND_DEFINITIONS_BY_FAMILY[family]; const familyHandlers = FAMILY_HANDLERS[family] as Readonly<Record<string, QueryHandler>>; const handlers: Record<string, QueryHandler> = {}; for (const entry of definitions) { const handler = familyHandlers[entry.handler_key!]; if (!handler) continue; handlers[entry.canonical] = handler; } return { family, aliases: definitions.map(toAliasCatalogEntry), handlers }; }这里可以看到handler_key的工程价值:当某个 canonical 的处理器在FAMILY_HANDLERS中注册名与 canonical 不一致时(例如合并处理器、共享处理器),通过显式handler_key即可让"装配别名组"与"绑定处理器"各司其职,而不需要复制命令元数据。这与[QUERY-HANDLERS.md](https://link.gitcode.com/i/33a1823bc064d2016d3a1e2374b44793)记录的"manifest-backed family ownership"(manifest 支撑的家族归属)相印证:各家族的别名产物与 CJS 镜像由command-manifest.*.ts生成,state/verify/init/phase/phases/validate/roadmap-command-router.cjs只保留薄适配器。
命令拓扑:从元数据到派发计划的桥接
Command Definition 派生的变更/输出语义被CommandTopology消费,定义于[sdk/src/query/command-topology.ts](https://link.gitcode.com/i/af224b20ba1409d58915d8c0340411db):
export interface CommandTopologyMatch { kind: 'match'; canonical: string; args: string[]; output_mode: CommandTopologyOutputMode; // 'json' | 'text' | 'raw' mutation: boolean; adapter: QueryHandler; }createCommandTopology(registry)([command-topology.ts](https://link.gitcode.com/i/3f4ec9a1895345280a5a8d6dc41a0a49))的resolve()流程:
- 取出
tokens[0]作为命令、其余作为参数; - 调用
resolveQueryCommand()([query-command-resolution-strategy.ts](https://link.gitcode.com/i/1e8d9b03a0298889c355d78090085786))做归一化与最长前缀匹配; - 匹配成功后,
output_mode通过supportsRawOutputCommand(matched.cmd)投影(命中COMMAND_RAW_OUTPUT_SET则为raw,否则json),mutation通过supportsMutationCommand(matched.cmd)投影(命中COMMAND_MUTATION_SET); - 无匹配时走
diagnoseUnknownCommand()产出结构化诊断(normalized / attempted / hints / message),并视fallbackRestricted追加GSD_QUERY_FALLBACK策略说明。
归一化策略本身([query-command-resolution-strategy.ts](https://link.gitcode.com/i/4caddc800ceecc74f49c08fcbeb2f25b))也值得注意:scaffold→phase.scaffold、空参数的state→state.load,以及state json→state.json这类子命令合并;matchRegisteredPrefix()([query-command-resolution-strategy.ts](https://link.gitcode.com/i/4506b4911970ba83c8632fdd7e688a7c))按 token 前缀从长到短同时尝试点号形式与空格形式,这与[QUERY-HANDLERS.md](https://link.gitcode.com/i/e03bbf0a1eb8327c3d4b66290b0f5e8b)记录的路由规则完全一致。
派发模式:native / cjs / error 三分支与--help守卫
runQueryDispatch()([sdk/src/query/query-dispatch.ts](https://link.gitcode.com/i/9136da26712a44b71b8c6e9e8d25dc67))依据planQueryDispatch()产出的DispatchPlan.mode分派:
- native:拓扑命中注册处理器,调用
nativeAdapter/dispatchNative或直接matched.adapter,成功后经formatSuccess()序列化(--pick字段抽取由extractField完成,见[sdk/src/query/registry.ts](https://link.gitcode.com/i/adb6a41a706b57b70a4b46ed05bbbd5a)); - cjs:未命中且
cjsFallbackEnabled,通过runCjsFallbackDispatch()([sdk/src/query/query-fallback-executor.ts](https://link.gitcode.com/i/0fad8ab93b4c22d5f75fe921de96604c))拉起gsd-tools.cjs子进程桥接,stderr 附带fallbackBridgeNotices()的桥接告警; - error:未命中且回退被禁用(如
GSD_QUERY_FALLBACK=off),返回unknown_command结构化错误。
一个值得单独说明的细节是mutating 命令的--help守卫([query-dispatch.ts](https://link.gitcode.com/i/92aaafc9e87b8a4aee6b70e8b204005e),注释标注 #3259):当调用包含--help/-h且命中的处理器是变更命令(mutation: true)时,直接短路返回非变更的用法 stub,防止milestone.complete --help这类调用意外把 milestone 产物写到磁盘。这个守卫能成立,正是因为它可以从拓扑匹配结果直接读到matched.mutation——也就是 Command Definition Module 派生的变更语义。
本次改动之二:CJS 回退错误映射清理
changeset 的第二项改动是:
query fallback error mapping cleanup — the CJS fallback catch path now passes original
errtomapFallbackDispatchError(对 PR #3066 中遗漏的既往评审意见的跟进)。
在[query-dispatch.ts](https://link.gitcode.com/i/7d87c6f35d0bfcdbb3a9afa4f82a5855)的 cjs 分支中,捕获到异常后现在原样把原始err传给mapFallbackDispatchError(e, normCmd, normArgs),而不是传一个重新构造的包装对象;runCjsFallbackDispatch的 catch 路径([query-fallback-executor.ts](https://link.gitcode.com/i/a6e5e3296194e451c6cf5f56397c033f))同样透传原始err。这样做的意义在于保留完整的原始错误信息(包括GSDToolsError的分类),让错误分类不再丢失上下文。
mapFallbackDispatchError的实现([query-dispatch.ts](https://link.gitcode.com/i/e03de379248796ae50247787ff487a4b))非常简洁:
export function mapFallbackDispatchError(error: unknown, command: string, args: string[]): QueryDispatchError { return fallbackDispatchErrorFromSignal(toFailureSignal(error), command, args); }toFailureSignal()([sdk/src/query-failure-classification.ts](https://link.gitcode.com/i/a5a748046e7dcaad71851b5346453c58))负责把任意异常归一为QueryFailureSignal:若原始错误是带classification的GSDToolsError,直接取用其分类(timeout/failure及timeoutMs);否则从消息文本中正则探测超时模式(/timed out after\s+(\d+)ms/i),最后兜底为failure。fallbackDispatchErrorFromSignal()([sdk/src/query/query-error-taxonomy.ts](https://link.gitcode.com/i/e79b14c4735168d3c5166a597142917b))再将其投影为fallback_failure错误:
{ kind: 'fallback_failure', code: 1, message: `Error: gsd-tools.cjs fallback failed: ${input.message}`, details: { command, args, backend: 'cjs' }, }结构化错误契约
[query-dispatch-contract.ts](https://link.gitcode.com/i/9563babbe09f407fc8aee31519f70b04)定义了派发结果的联合契约:
- 成功:
{ ok: true, stdout, stderr, exit_code: 0 } - 失败:
{ ok: false, error: { kind, code, message, details }, stderr, exit_code }
其中error.kind是封闭的六值枚举([query-dispatch-contract.ts](https://link.gitcode.com/i/9563babbe09f407fc8aee31519f70b04#L1-L7)),与[CONTEXT.md](https://link.gitcode.com/i/309f095688c9b981dd9c83ddd3084d1b)及[docs/adr/0001-dispatch-policy-module.md](https://link.gitcode.com/i/a4c333644ccfd34be384c52736fa7b9f#L6)记录的规范错误集一致:
unknown_command | native_failure | native_timeout | fallback_failure | validation_error | internal_errorquery-dispatch-error-mapper.ts([sdk/src/query/query-dispatch-error-mapper.ts](https://link.gitcode.com/i/bbb33dfe8d8a38bcddb7cb39a4dd80da))如今只是一个标注@deprecated的兼容性再导出接缝("Remove-after: all imports migrate toquery-dispatch.ts"),这正是"错误映射收敛到 Dispatch Policy Module"的落点。
测试防线:契约测试与反向测试
本次改动在[sdk/src/query/query-dispatch.test.ts](https://link.gitcode.com/i/df07357959c710c1b044fc1e707c33b1)中有直接验证:
it('mapFallbackDispatchError maps spawn errors to fallback_failure with details', () => { const err = mapFallbackDispatchError(new Error('spawn ENOENT'), 'state', ['load']); expect(err.kind).toBe('fallback_failure'); expect(err.code).toBe(1); expect(err.details).toMatchObject({ command: 'state', args: ['load'], backend: 'cjs' }); }); it('counter: mapFallbackDispatchError does not produce native_failure kind', () => { const err = mapFallbackDispatchError(new Error('spawn ENOENT'), 'state', []); expect(err.kind).toBe('fallback_failure'); expect(err.kind).not.toBe('native_failure'); });这类"反向测试"(counter-test)是该仓库测试风格的典型特征:不仅要验证正确行为,还要显式锁定"错误分类不会串类"的边界。测试还覆盖了cjs模式选择([query-dispatch.test.ts](https://link.gitcode.com/i/60f105de886d6b61a68e89d47638b0b5))、resolveGsdToolsPath抛错时仍映射为fallback_failure([query-dispatch.test.ts](https://link.gitcode.com/i/d239ad7b7b95646c2af0efab6b61be07))等场景。
命令定义侧的契约测试见[sdk/src/query/command-definition.test.ts](https://link.gitcode.com/i/afd12bcb87bcab595f4f860bd0fa77cd):COMMAND_DEFINITIONS长度必须等于两个清单长度之和、handler_key默认回退 canonical、家族索引与家族定义 canonical 集合一致、派生的 mutation/raw 集合命中预期的点号与空格别名。这些断言使得"单一数据源"不会在后续演进中悄悄失守。
与既有架构文档的对应关系
- ADR-0001(
[docs/adr/0001-dispatch-policy-module.md](https://link.gitcode.com/i/a4c333644ccfd34be384c52736fa7b9f))确立"查询派发结果集中在单一 Dispatch Policy Module,返回结构化联合结果",本次 Command Definition Module 收敛正是该方向的接缝加深; - CONTEXT.md的模块清单(
[CONTEXT.md](https://link.gitcode.com/i/5f202c5de30d52500102761b4d68940a))将 Command Definition Module、Command Topology Module、Query Command Resolution Module 等并列登记,并记录其"深度政策模块 + 薄适配器"的职责划分; - QUERY-HANDLERS.md(
[sdk/src/query/QUERY-HANDLERS.md](https://link.gitcode.com/i/b257c0756963fee103f90aaa25a358db))补充了 manifest 家族归属、路由规则、错误处理约定与 golden parity 覆盖策略,可作为本模块的配套契约文档阅读。
小结:一次"收敛"带来的工程收益
brave-mice-build.md这则 changeset 表面只是两行改动描述,实质是查询层架构的两处收敛:
- 元数据收敛:
family/canonical/aliases/mutation/output_mode/handler_key六个字段成为注册表装配、变更语义、别名生成的唯一接口输入,所有派生视图(mutation 集合、raw 输出集合、家族索引、canonical 索引)均由[sdk/src/query/command-definition.ts](https://link.gitcode.com/i/f05e00ff0bd3cdffe7895c7ebda9afd2)一处生成——局部性提升、漂移风险结构性降低; - 错误映射收敛:CJS 回退 catch 路径原样透传原始
err给mapFallbackDispatchError,错误分类(fallback_failure,code 1,backendcjs)与契约保持稳定,配套测试锁定其不产生native_failure串类。
对于在 get-shit-done 上二次开发、或研究其 SDK 查询层架构的读者,本文提到的文件路径(command-definition.ts→command-manifest.*.ts→registry-assembly-descriptor.ts→command-topology.ts→query-dispatch.ts→query-error-taxonomy.ts→query-failure-classification.ts)构成了一条可以完整追踪的调用链,也是理解"政策深度集中、适配器轻薄"设计哲学的绝佳入口。
- 人工智能
- AI 应用
- 提示工程
- 开发工具
- 工作流自动化
- AI Agent
【免费下载链接】get-shit-done
A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.
相关推荐
gsd-core 查询命令元数据统一:Command Definition Module 接缝与 CJS 回退错误映射解析
gsd core 查询命令元数据统一:Command Definition Module 接缝与 CJS 回退错误映射解析 导读 本文围绕仓库归档变更记录 .c
get-shit-done CJS↔SDK 硬接缝迁移:Shared-Module 单一事实源架构实践
get shit done CJS↔SDK 硬接缝迁移:Shared Module 单一事实源架构实践 导读 本文以 docs/prd/3524 cjs sdk
人工智能AI 应用提示工程开发工具工作流自动化AI Agentget-shit-done 的 CommandRoutingHub 设计解析:用单一派发接缝收敛 SDK/CJS 双模式命令路由
get shit done 的 CommandRoutingHub 设计解析:用单一派发接缝收敛 SDK/CJS 双模式命令路由 导读 CommandRouti
人工智能AI 应用提示工程开发工具工作流自动化AI Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考