ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

oh-my-pi Rulebook 匹配管道:从多格式规则发现、归一化、优先级仲裁到 TTSR 分桶的实现全解析

oh-my-pi Rulebook 匹配管道:从多格式规则发现、归一化、优先级仲裁到 TTSR 分桶的实现全解析 oh-my-pi Rulebook 匹配管道从多格式规则发现、归一化、优先级仲裁到 TTSR 分桶的实现全解析【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi本文以 oh-my-pi 项目中 docs/rulebook-matching-pipeline.md 为骨架结合packages/coding-agent/src下的真实源码完整剖析 coding-agent 如何从 OMP、Agent、Cursor、Windsurf、Cline、GitHub Copilot 等八类配置源中发现规则将其归一化为统一的Rule形状按优先级去重后拆分为「Rulebook 规则」通过系统提示与rule://URL 提供给模型与「TTSR 规则」Time Traveling Stream Rules用于流式触发中断两个出口。读完本文你将掌握每条规则元数据globs、alwaysApply、agents、condition、astCondition、scope、interruptMode在整条管道中的真实作用边界以及哪些语义当前只解析、不强制执行。1. 统一规则形状一切来源最终都是Rule所有 provider 最终都把各自的源文件归一化为同一个Rule结构。该类型定义于 packages/coding-agent/src/capability/rule.tsinterface Rule { name: string; path: string; content: string; globs?: string[]; alwaysApply?: boolean; description?: string; condition?: string[]; astCondition?: string[]; scope?: string[]; agents?: string[]; interruptMode?: never | prose-only | tool-only | always; _source: SourceMeta; }各字段含义与来源字段说明name规则标识通常由文件名去掉扩展名得到Capability 的 key 即rule.namepath规则文件的绝对路径content剥离 frontmatter 后的正文globs该规则适用的文件路径模式alwaysApply是否无条件注入系统提示description规则描述是进入 Rulebook 列表的必要条件condition正则形式的 TTSR 触发条件新键兼容旧ttsr_trigger/ttsrTriggerastConditionast-grep 结构化模式触发条件仅作用于 edit/write 工具流scopeTTSR 匹配的流表面白名单text/thinking/tool/tool:xxxagents该规则适用的 Agent 名称 glob小写化interruptMode单规则级 TTSR 中断模式覆盖_source来源元数据provider、路径、user/project 级别ruleCapability的定义在 packages/coding-agent/src/capability/rule.tskey: rule rule.name。一个直接后果是优先级仲裁与去重完全基于name——两个不同文件只要name相同就被视为同一条逻辑规则先到者胜出后到者被标记为_shadowed。这一点贯穿整个管道的所有阶段。2. 八类规则发现源及其归一化packages/coding-agent/src/discovery/index.ts 通过 import 副作用自动注册全部 provider。对rules能力而言当前生效的 provider 及其优先级如下高者先优先级Provider源文件100nativepackages/coding-agent/src/discovery/builtin.ts90omp-pluginspackages/coding-agent/src/discovery/omp-plugins.ts70agentspackages/coding-agent/src/discovery/agents.ts50cursorpackages/coding-agent/src/discovery/cursor.ts50windsurfpackages/coding-agent/src/discovery/windsurf.ts40clinepackages/coding-agent/src/discovery/cline.ts30githubpackages/coding-agent/src/discovery/github.ts1builtin-defaultspackages/coding-agent/src/discovery/builtin-defaults.ts同优先级50时按注册顺序即cursor先于windsurf。绝大多数 provider 复用 packages/coding-agent/src/discovery/helpers.ts 中的buildRuleFromMarkdown/discoverRuleFromMarkdown共享归一化路径文件名派生name、剥离 frontmatter 的content、解析globs/alwaysApply/description/condition/astCondition/scope/agents/interruptMode。注意discoverRuleFromMarkdown与buildRuleFromMarkdown的唯一区别前者在 frontmatter 中enabled: false时返回null即被显式禁用helpers.ts。2.1 native provider.omp优先级 100builtin.ts是 OMP 的原生配置源builtin.ts规则加载顺序如下项目规则当 cwd 的.omp/目录非空时加载cwd/.omp/rules/*.{md,mdc}用户规则active-native-agent-dir/rules/*.{md,mdc}用户粘性规则active-native-agent-dir/RULES.md项目粘性规则从 cwd 向仓库根向上查找最近的、非空的.omp/目录中的RULES.mdOMP 不会越过该目录继续向上查找。其中 active native agent 目录默认是~/.omp/agent跟随命名 profilegetAgentDir()并可通过PI_CODING_AGENT_DIR环境变量重定向。粘性RULES.md会被合成为强制alwaysApply: true的规则即每次会话轮次都重新注入以在长对话中保持约束效力。这里有一处值得注意的源码与文档的差异文档写作时称两个粘性文件都用固定名RULES而当前源码builtin.ts为const ruleName level project ? RULESproject : RULES;即用户级粘性规则名为RULES项目级粘性规则名为RULESproject。在按名去重的模型下这一命名意味着用户粘性内容与项目粘性内容不会互相遮蔽而普通的rules/RULES.md仍可覆盖两者。关键 caveatfrontmatter 中形如文件 glob 的condition值会被转换为tool:edit(...)/tool:write(...)的 scope 简写并配以兜底条件.*详见 parseRuleConditionAndScope。2.2 omp-plugins provider优先级 90从配置好的扩展包根目录内加载rules/*.{md,mdc}同样走共享的buildRuleFromMarkdown归一化路径按每个扩展包根目录依次追加结果。2.3 agents provider.agent / .agents优先级 70同时支持.agent与.agents两种目录约定项目级从 cwd 向仓库根向上遍历加载每个祖先目录的ancestor/.agent/rules/*.{md,mdc}与ancestor/.agents/rules/*.{md,mdc}用户级~/.agent/rules/*.{md,mdc}与~/.agents/rules/*.{md,mdc}。加载顺序为项目遍历结果在前、用户主目录结果在后。归一化走共享 Markdown 路径字段解析与 native 一致。2.4 cursor provider.cursor优先级 50从以下位置加载用户级~/.cursor/rules/*.{mdc,md}项目级cwd/.cursor/rules/*.{mdc,md}归一化由 cursor.ts 的transformMDCRule完成关键规则description仅当为字符串时保留alwaysApply严格布尔归一化——仅当 frontmatter 明确为alwaysApply: true时才为true其余一切值包括字符串true都变成falseglobs接受字符串数组仅保留字符串元素或单个字符串condition/ttsr_trigger、astCondition、scope、agents、interruptMode均由共享 rule helpers 解析name由文件名去扩展名得到。顺序上 user 结果先、project 结果后。2.5 windsurf provider.windsurf优先级 50用户级~/.codeium/windsurf/memories/global_rules.md规则名固定为global_rules项目级cwd/.windsurf/rules/*.md规则名取自文件名。用户级文件先加载项目级后加载。其余字段由共享 helpers 解析windsurf.ts。2.6 cline provider.clinerules优先级 40只支持项目级配置从 cwd 向上查找最近的一个.clinerulescline.ts若是目录加载其中全部*.md规则名取自文件名若是文件作为单条规则加载规则名固定为clinerules。2.7 github provider.github/instructions优先级 30递归加载*.instructions.md项目级cwd/.github/instructions/用户级对COPILOT_CUSTOM_INSTRUCTIONS_DIRS逗号分隔的目录列表中的每个目录加载dir/.github/instructions/。文件名去掉.instructions.md后缀即规则名。共享 Markdown 解析仍然识别 OMP 规则元数据含 TTSR 字段。GitHub 的applyTo另有专门归一化逻辑逗号分隔字符串或容错的 YAML 数组→globs*、**或**/*→ 规则变为 always-apply 并清空globs其他 glob → 规则非 always-apply若缺少description则根据 globs 自动生成缺少applyTo→ 生成一条 rulebook 描述外加一条发现警告。特别注意由于 TTSR 分桶发生在 always-apply/rulebook 分桶之前携带了被接受的condition或astCondition的 GitHub instruction无论applyTo如何都只属于 TTSR。2.8 builtin-defaults provider优先级 1随 agent 内置的默认规则集优先级最低任何同名用户/项目/工具规则都会覆盖内置默认。其BUILTIN_DEFAULTS_PROVIDER_ID builtin-defaultsrule.ts并可通过ttsr.builtinRules配置整体开关。加载顺序为内置规则源本身的嵌入顺序。3. frontmatter 解析行为与歧义处理所有 provider 都经由 packages/utils/src/frontmatter.ts 的parseFrontmatter其语义是仅在内容以---开头且有闭合的\n---时才解析 frontmatter否则整份文件按正文处理frontmatter.ts。提取 frontmatter 后正文会被trim()。若整篇 YAML 解析失败记录一条警告回退到简单的key: value行解析正则^([\w-]):\s*(.*)$每个捕获到的值独立地再按 YAML 重解析一次只有仍然解析失败的值才保留为原始 trim 字符串frontmatter.ts。回退解析的边界情况多行数组、嵌套对象等依赖缩进的 YAML 结构无法重建但合法的单行 flow 值如[text, thinking]可以在逐值重解析中存活单个格式错误的值保持原始字符串需要布尔/列表/对象的 provider 可能会丢弃该元数据下划线键ttsr_trigger在回退路径中可用连字符键如thinking-level也能解析并被归一化为 camelCasethinkingLevel——键归一化同样作用于 YAML 成功路径normalizeFrontmatterKeys见 frontmatter.ts没有合法 frontmatter 的文件仍会以空元数据 完整正文的形式作为规则加载scope 解析器还能容忍常见的畸形回退值scope: text,thinking但规范写法仍是scope: text, thinking逗号在字符串内或scope: [text, thinking]YAML 序列。4. Provider 优先级与按名去重loadCapability(rules)合并各 provider 输出后按rule.name去重入口在 packages/coding-agent/src/capability/index.ts。4.1 优先级模型provider 按 priority 降序排列同优先级保持注册顺序cursor在windsurf之前去重为先到先得first-wins先遇到的规则名被保留后续同名项在all中标记为_shadowed并从items中剔除。因此实际生效的规则 provider 顺序就是第 2 节表格中的顺序。4.2 provider 内部的顺序 caveatprovider 内部顺序来自loadFilesFromDir的 glob 结果顺序加上显式 push 顺序。这在常规使用下是确定性的但代码中并未显式排序。各来源的追加顺序差异native项目.omp/rules→ 用户~/.omp/agent/rules→ 用户RULES.md→ 最近项目RULES.mdomp-plugins按每个配置的扩展包根目录依次追加rules/结果agents项目遍历的.agent/.agents规则目录在前用户主目录在后cursor用户结果在前项目结果在后windsurf用户global_rules在前项目规则在后cline只加载最近的.clinerules源githubcwd 项目 instructions 在前随后按环境变量列表顺序追加各COPILOT_CUSTOM_INSTRUCTIONS_DIRS条目builtin-defaults内置规则源的嵌入顺序。5. 分桶Rulebook、Always-Apply 与 TTSR规则发现完成后packages/coding-agent/src/capability/rule-buckets.ts 中的bucketRules(...)在会话创建createAgentSession见 packages/coding-agent/src/sdk.ts时执行会话级过滤与分桶共六步丢弃ttsr.disabledRules中列出的规则当ttsr.builtinRules false时丢弃来自builtin-defaultsprovider 的全部规则丢弃agentsglobs 与当前会话 agent 名不匹配的规则顶层会话 agent 名为main子会话为 agent 定义名无agents字段的规则适用于所有 agent将condition或astCondition非空的规则注册进TtsrManager注册成功即该规则仅属于 TTSR其余alwaysApply true的规则进入alwaysApplyRules其余带description的规则进入rulebookRules。对应实现rule-buckets.tsfor (const rule of rules) { if (disabled.has(rule.name)) continue; if (!includeBuiltin rule._source?.provider BUILTIN_DEFAULTS_PROVIDER_ID) continue; if (!ruleAppliesToAgent(rule, options.agentName)) continue; const hasTtsrCondition (rule.condition rule.condition.length 0) || (rule.astCondition rule.astCondition.length 0); const isTtsrRule hasTtsrCondition ? ttsrManager.addRule(rule) : false; if (isTtsrRule) continue; if (rule.alwaysApply true) { alwaysApplyRules.push(rule); continue; } if (rule.description) { rulebookRules.push(rule); } }5.1 各桶的行为要点TTSR 桶任何启用且带非空condition正则或astConditionast-grep 模式且被TtsrManager.addRule(...)接受的规则。优先级最高先于其他桶判断。Always-apply 桶alwaysApply true且非 TTSR。完整内容注入系统提示同时可通过rule://读取。Rulebook 桶必须有description、非 TTSR、非 always-apply。系统提示只列出name description正文通过rule://按需读取。边界情形同时带触发条件与alwaysApply的规则只有 TTSR 注册拒绝它时才可能落到 always-apply同时带alwaysApply与description的规则只进 always-apply不进 rulebook。6. 元数据对运行时各表面的影响6.1description进入 rulebook 的必要条件渲染在系统提示的 rulebook 区块默认模板为domain-rules自定义提示模板为rules缺失 description 的规则不进 rulebook 列表除非它是 always-apply 或被接受的 TTSR 规则否则也无法通过rule://寻址。6.2globs随Rule原样携带默认提示的 rulebook 列表中以内联形式渲染- name (glob, ...): description自定义提示模板渲染为glob.../glob条目暴露在规则 UI 状态extensions 模式列表中被 TTSR 用作全局路径门若 TTSR 规则带 globs匹配上下文必须包含至少一个匹配的文件路径不用于为rule://自动挑选 rulebook 规则——rulebook 的匹配仍是提示层面的建议行为。6.3alwaysApplyprovider 解析并保留UI 中显示为always触发标签作为排除出rulebookRules的条件规则全文自动注入系统提示位于 rulebook 规则区块之前也可通过rule://name重新读取。6.4agents把规则限定到特定 Agent接受 YAML 序列、单个字符串或逗号分隔字符串模式为小写化 glob对 agent 定义名scout、reviewer、foreman-*做大小写不敏感匹配。{a, b}花括号 glob 组内逗号两侧的空格会被容忍并归一化parseRuleAgents复用 scope tokenizer见 rule.ts。字面量main匹配顶层会话无定义名的子 agent 回退为sub。main与sub均为保留哨兵parseAgentFields拒绝自定义 agent 使用这两个名字helpers.ts因此真实 agent 永远无法遮蔽哨兵。省略或空列表表示规则适用于所有 agent——即既有行为。过滤在会话创建时的bucketRules(...)中、TTSR 注册之前执行一次不匹配的规则不进任何桶、不会被编译进TtsrManager、在该会话中也无法通过rule://寻址。子 agent 会收到父级未过滤的完整规则列表并以其自身名字重新评估agents因此 scout-only 的规则只在 scout 中加载。agents: [scout, foreman-*]# 仅主 agent所有子 agent 忽略此规则 agents: main6.5condition、astCondition、scope、interruptModecondition正则 TTSR 触发字段解析时接受旧键ttsr_trigger/ttsrTrigger作为回退输入。开头为(?i)、(?m)或(?s)的内联标志组会被翻译为等价的 JavaScriptRegExp标志compileRuleCondition见 rule.ts——因为 Bun/JS 的RegExp拒绝内联标志前缀若无此翻译condition: (?i)pre.existing会在编译期抛错并被静默丢弃。astConditionast-grep 触发字段字符串或 YAML 模式序列原样保留不做 glob 推断只在 edit/write 工具流上匹配语言由文件路径推断。一条规则可以同时设置condition与astCondition。scope把 TTSR 匹配收窄到流表面白名单接受逗号分隔的 YAML 字符串或 YAML 序列。省略时监视助手散文text与全部工具参数tool但不监视 thinking。# 散文与思考两种等价写法 scope: text, thinkingscope: [text, thinking]# 块式 YAML 序列同样合法 scope: - text - thinking# 仅 edit/write 产生的 TypeScript 源码快照 scope: tool:edit(*.ts), tool:write(*.ts)合法 token 为text、thinking、tool或toolcall与tool:name(path-glob)。解析器容忍畸形回退拼写scope: text,thinking但可移植的规则文件应把逗号放进单个 YAML 字符串或使用 YAML 序列。条件里的文件 glob 简写形如文件 glob 的conditiontoken 会变成tool:edit(glob)与tool:write(glob)两条 scope 条目外加兜底条件.*astConditiontoken 永不触发此简写。启发式判断函数isLikelyFileGlob见 rule.ts含正则元字符\^$|()的不算不含?*[]{}的不算含/的直接算 glob否则要求形如^\*\.[^\s/]$如*.rs。interruptMode可覆盖全局 TTSR 中断模式取值never | prose-only | tool-only | always非法值被丢弃helpers.ts。7. 系统提示注入路径buildSystemPromptInternal同时接收rulesrulebook与alwaysApplyRules实现在 packages/coding-agent/src/system-prompt.ts。always-apply 规则会先与生效的 system/custom/append 提示源及已加载的 context-file 正文做去重某条规则的归一化内容已出现在上述任一来源中时跳过自动注入。剩余原始正文渲染在 rulebook 列表之前——默认模板放进generic-rules打包的自定义提示模板则直接渲染。rulebook 规则渲染在domain-rules块格式为- name (globs): description提示中的 URL 列表记录rule://name工作流章节要求模型先读取相关规则。自定义提示模板custom-system-prompt.md则以rule name...条目 glob子元素渲染并带显式的 You MUST readrule://name 指令。需要明确这是建议性/上下文性行为——提示文本请求模型读取适用规则但代码并不强制校验 glob 适用性。8.rule://内部 URL 行为packages/coding-agent/src/internal-urls/rule-protocol.ts 的RuleProtocolHandler针对进程级 active-rule 快照解析该快照在每次顶层会话创建时由 sdk.ts 安装一次setActiveRules([ ...rulebookRules, ...alwaysApplyRules, ...ttsrManager.getRules(), ]);由此产生以下行为rule://name可解析rulebookRules、alwaysApplyRules与已注册的 TTSR 规则三者TTSR 规则虽然已从 rulebook/always 中分桶出去但ttsrManager.getRules()会把它们重新加回快照使一条被触发的规则例如内置规则仍可被重新读取没有 description、没有alwaysApply、也没有被接受的 TTSR 条件的规则无法通过rule://寻址解析为精确名称匹配rules.find(r r.name ruleName)未知名称返回错误并在错误信息中列出全部可用规则名Unknown rule: ...\nAvailable: ...返回内容是原始rule.contentfrontmatter 已剥离内容类型为text/markdown。9. 已知的部分语义 / 未强制执行的语义文档与源码共同确认以下边界避免使用者产生不切实际的预期当前为rules加载的 provider 是native、omp-plugins、agents、cursor、windsurf、cline、github与内置builtin-defaults其他工具的 provider 文件可能解析其他配置格式但没有注册规则加载器。globs元数据暴露给提示/UI并作为 TTSR 匹配的全局路径门但不用于为rule://自动挑选 rulebook 规则。rule://的规则选择包含 rulebook、always-apply 与已注册 TTSR 规则因此被触发的 TTSR 规则可重读但不包含既无触发条件、又无description与alwaysApply的规则。发现警告loadCapability(rules).warnings会产生但createAgentSession目前在这条路径上不对外展示或记录它们。10. 从文档到源码的核对清单想要亲手验证上述每一环可直接按图索骥统一形状与字段解析packages/coding-agent/src/capability/rule.ts分桶漏斗packages/coding-agent/src/capability/rule-buckets.ts共享 Markdown 归一化与 glob 扫描packages/coding-agent/src/discovery/helpers.ts各 providerbuiltin.ts、omp-plugins.ts、agents.ts、cursor.ts、windsurf.ts、cline.ts、github.ts、builtin-defaults.tsfrontmatter 解析与回退packages/utils/src/frontmatter.tsrule://协议处理器packages/coding-agent/src/internal-urls/rule-protocol.ts会话创建与快照安装packages/coding-agent/src/sdk.ts系统提示注入packages/coding-agent/src/system-prompt.tsTTSR CLI 入口packages/coding-agent/src/cli/ttsr-cli.ts以 4.1 的「先到先得」与 5.1 的「TTSR 优先」两条规则为心智锚点再对照第 6 节各元数据的真实作用边界即可对 oh-my-pi 的规则系统建立完整、可预测的理解文件名决定身份优先级决定胜负条件字段决定归属其余元数据决定运行时表现——而其中相当一部分如 rulebook 的 glob 匹配仍是面向模型的建议语义而非代码强制行为。【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表