oh-my-pi 编码代理 glob 工具深度指南:模式匹配、内部 URL 与路径发现原理
2026/9/12 3:17:50 网站建设 项目流程

oh-my-pi 编码代理 glob 工具深度指南:模式匹配、内部 URL 与路径发现原理

【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi

本文围绕 oh-my-pi(⌥ Coding agent with the IDE wired in)编码代理内置的glob工具展开,以工具提示词文档 packages/coding-agent/src/prompts/tools/glob.md 为核心骨架,结合 GlobTool 实现、路径解析工具、原生 glob 编译实现 与测试用例,系统讲解glob工具的完整能力:如何用 glob 模式、文件、目录与内部 URL 做快速路径匹配,如何控制 gitignore/hidden 过滤,以及输出排序、超时降级、根目录保护等底层行为。读完本文,你将掌握在 oh-my-pi 会话中高效检索项目文件的全部参数用法与实现原理。

一、工具定位:agent 的文件系统"眼睛"

在 oh-my-pi 的编码代理工具集中,globgrepreadbash共同构成文件系统探索的基本盘。glob 负责"找到匹配路径",其提示词只有一句话点明职责:

Globs files, directories, and path-backed internal URLs with fast pattern matching.

对应到实现,GlobTool类(packages/coding-agent/src/tools/glob.ts)声明了三个关键属性:

  • approval = "read":属于只读操作,不触发写权限审批;
  • loadMode = "essential":核心工具,随会话基础加载;
  • strict = true:严格校验入参,非法输入直接报错。

它同时也是"find"类工具的前端别名之一——在 builtin-names.ts 中["find", "glob"]被归为一组,表明历史上 find 的职责已并入 glob。

二、path 参数:一个参数覆盖四种目标

提示词文档明确了path参数的取值范畴:glob 模式、文件、目录或 path-backed 内部 URL。Schema 定义见 glob.ts:

const findSchema = type({ "path?": type("string").describe( 'glob, file, or directory to search — a single path or a semicolon-delimited list ("src/**/*.ts; test/**/*.ts"). Omitted -> searches the workspace root (".")', ), "hidden?": type("boolean").describe("include hidden files"), "gitignore?": type("boolean").describe("respect gitignore"), "limit?": type("number").describe("max results"), });
  • 省略path时等价于传入".",即搜索工作区根目录(见 glob.ts 的effectivePaths默认值逻辑);
  • 传入普通目录时,工具会递归匹配其下所有文件(parseFindPattern将无 glob 字符的目录解析为basePath + "**/*");
  • 传入单个文件路径时,通过stat判断isFile()直接返回该文件,不做目录扫描(glob.ts)。

2.1 分号分隔多目标

提示词文档给出的关键语法是分号分隔:src/**/*.ts; test/**/*.ts。实现中的解析远比这丰富——path-utils.ts 的路径拆分器支持三种顶层分隔符:逗号,、分号;与空白,且具备花括号深度感知:{a,b}内部的逗号不会被误判为路径分隔符。因此以下写法均合法:

src/**/*.ts; test/**/*.ts // 分号分隔 src/**/*.ts, test/**/*.ts // 逗号分隔 src/**/*.ts test/**/*.ts // 空白分隔 *.{ts,tsx} // 花括号展开,内部逗号不参与拆分

另外toPathList还支持 JSON 编码的字符串数组'["a.ts","b.ts"]'(path-utils.ts),保证模型输出数组形式入参时也能被正确解析。

2.2 多目标的部分缺失容忍

当一次调用传入多个路径、其中某些路径的基目录在磁盘上已不存在时,工具不会整体失败:它会跳过缺失项并继续扫描剩余路径,仅在所有目标全部缺失时才抛出Path not found错误(glob.ts)。被跳过的路径会以Skipped missing paths: ...的形式作为非致命警告附加在结果文本与 TUI 渲染中(GlobToolDetails.missingPaths字段)。这是刻意设计的容错:多路径调用中一个目录被删除不应拖垮整个检索。

2.3 模式解析规则

parseFindPattern(path-utils.ts)负责把用户输入拆成"搜索基目录 + glob 模式"两部分:

src/app/**/*.tsx → { basePath: "src/app", globPattern: "**/*.tsx", hasGlob: true } *.ts → { basePath: ".", globPattern: "**/*.ts", hasGlob: true } **/*.json → { basePath: ".", globPattern: "**/*.json", hasGlob: true } /abs/path/**/*.ts → { basePath: "/abs/path", globPattern: "**/*.ts", hasGlob: true } src/app → { basePath: "src/app", globPattern: "**/*", hasGlob: false }

注意*.ts会被自动补成**/*.ts实现递归匹配——这意味着裸 glob 默认就是全子树递归。而显式给出目录前缀的src/*.ts则只在该目录层级匹配,不会穿透子目录。

三、gitignore 与 hidden:默认全开,按需关闭

提示词文档给出了两条关键默认值:

参数默认值作用
gitignoretrue尊重.gitignore,跳过被忽略文件
hiddentrue包含隐藏文件(点文件)

对应实现见 glob.ts:

const includeHidden = hidden ?? true; const useGitignore = gitignore ?? true;

由此可以推导出三种典型组合:

  • 默认行为gitignore: true+hidden: true—— 跳过 git 忽略文件,但依然能搜到.env这类点文件(因为 hidden 默认开启);
  • 搜索被 git 忽略的文件(日志、构建产物):{ path: ".env*", gitignore: false }—— 这是提示词文档与内置示例共同推荐的用法;
  • 搜索被 git 忽略的点文件{ path: ".cache", gitignore: false }—— 提示词文档特别强调"pair it withgitignore: falsefor ignored dotfiles",因为仅关 hidden 是不够的,.gitignore 规则可能同时覆盖这些文件。

内置示例(glob.ts)还包含一个目录匹配示范:{ path: "**/tests" }返回所有名为 tests 的目录(目录以/结尾标识)。

四、内部 URL:memory:// 与 ssh:// 的特殊规则

提示词文档关于内部 URL 的规则是本文最容易被忽略、也最重要的部分:

  • memory://glob 模式受支持:可以直接用memory://前缀配合 glob 通配符检索记忆资源;
  • ssh://没有本地路径:glob 无法操作,必须改用read工具;
  • 其他内部 URL 只接受精确路径:带 glob 字符会直接报错。

这些规则在 glob.ts 中有着严格的类型化实现:

  1. InternalUrlRouter.instance().canHandle(rawPattern)识别内部 URL 前缀;
  2. isSshUrl(rawPattern)命中时抛出明确的引导错误:
find cannot operate on a remote ssh:// path: <path>. ssh:// has no local file to glob; use `read <path>` to list or inspect the remote path.
  1. 非 memory 内部 URL 若包含 glob 字符(*?[{),抛出Glob patterns are not supported for internal URLs
  2. memory://模式通过splitMemoryGlobPattern拆分为 baseUrl 与 glob 部分,经internalRouter.resolve(..., { pathOnly: true })解析出真实 backing 文件路径后,再将 glob 拼接上去执行扫描。通配符在拼接前会被转义(replace(/[*?[{]/g, "[$&]")),避免影响 base 部分。

五、输出格式:最新优先、按目录分组

提示词文档定义了输出契约:

Matches are newest-first and grouped by directory; directories end in/.

实现上由三条链路共同保证:

  1. 原生扫描按 mtime 排序:调用natives.glob时传入sortByMtime: true(glob.ts),让最近修改的文件排在最前;
  2. 多根合并后再全局重排:每个目标的扫描并发执行、各自按 mtime 排名并截断到 limit,最后在 JS 层去重、按 mtime 降序合并(glob.ts),确保跨多个搜索根的整体 top-N 正确;
  3. 目录以/结尾formatMatchPath根据原生返回的FileType.Dir或路径自带尾斜杠决定是否补/(glob.ts),分组渲染由formatGroupedPaths完成。

输出对模型表现为普通文本(No files found matching pattern表示零命中);对 TUI 用户则由globToolRenderer渲染为内联状态行 + 文件树列表,支持折叠/展开与 OSC 8 超链接跳转(见 glob.ts)。

六、limit 与超时:宁可给部分结果,不抛空错误

limit参数有硬性上限:DEFAULT_LIMIT = 200MAX_LIMIT = 200(glob.ts),有效值取Math.min(200, Math.max(1, floor(requestedLimit)))。非正数 limit 会抛出Limit must be a positive number

超时方面,默认扫描超时为5000msDEFAULT_GLOB_TIMEOUT_MS,可在GlobToolOptions.timeoutMs中覆盖,仅测试用)。超时的行为设计非常讲究(glob.ts):

  • 绝不把超时当作"无匹配"的证据:空结果 + 超时会附加glob timed out after Ns before finding any matches — the scan is incomplete, NOT proof of absence,避免误导后续决策;
  • 有部分匹配时返回部分结果:把流式累积的匹配按 mtime 排序后返回,并注明returning N partial matches — results are incomplete
  • 提示正确补救方向:文档和实现都强调"walk cost 取决于目录树规模而非模式宽度",因此建议sub/dir/*.ext而非在大根目录上写*.ext——这与 walk_depth_bound 的深度裁剪机制互为表里。

七、底层原理:原生 glob 的编译与快速路径

GlobTool默认调用natives.glob@oh-my-pi/pi-natives的原生绑定),其匹配器位于 crates/pi-natives/src/glob_util.rs。三处关键设计:

  1. 模式规范化(build_glob_pattern):统一/分隔符、按需补**/前缀、自动闭合未闭合的{花括号组;
  2. 字面分隔符语义:编译时使用literal_separator(true),保证*?[...]永不跨越/,避免模式意外匹配到深层子目录;
  3. 快速路径分流(CompiledGlob::is_match):GlobFastPath枚举覆盖All**全匹配)、RootOnly(仅根层)、Extension/RootExtension(扩展名快判)、Basename/RootBasename(文件名快判)等常见形态,只有无法归类的模式才落到通用GlobSet匹配,兼顾速度与通用性。

另外GlobOperations接口(glob.ts)允许把exists/stat/glob三个操作注入为远程实现,注释明确说明这是为 SSH 等远程文件系统预留的扩展点;注入自定义操作时,工具会传入固定的忽略列表["**/node_modules/**", "**/.git/**"](glob.ts)。

八、安全护栏与取消语义

  • 禁止根目录搜索path: "/""//"会直接抛出Searching from root directory '/' is not allowed(glob.ts,测试见 glob.test.ts),防止全盘扫描拖垮会话;
  • 准备阶段与扫描阶段分离取消:stat 准备阶段使用独立 AbortController,原生扫描启动后才把真实调用方信号接管过去(glob.ts),保证取消及时且不泄漏监听器;
  • 超时与取消的竞态处理:测试明确验证了"超时后必须等原生扫描真正停止才结束调用""中止时必须等待所有并发扫描都 settle 后才抛出ToolAbortError"两条不变量(glob.test.ts),避免悬挂的原生任务污染后续会话。

九、实战速查

场景调用示例
递归找所有 TS 文件{ "path": "src/**/*.ts" }
多目录同时检索{ "path": "src/**/*.ts; test/**/*.ts" }
找被 git 忽略的 .env 文件{ "path": ".env*", "gitignore": false }
找被忽略的点文件目录{ "path": ".cache", "gitignore": false }
找同名目录{ "path": "**/tests" }
精确文件命中{ "path": "src/main.ts" }(返回单个文件)
限制返回数量{ "path": "**/*.json", "limit": 50 }

实践要点:裸 glob 默认递归全树且按 mtime 排序;大目录下优先把模式写深(sub/dir/*.ext)以避免超时;ssh://路径请改用read;搜索node_modules、构建产物等被忽略内容必须显式gitignore: false

十、延伸阅读

  • 工具提示词原文:packages/coding-agent/src/prompts/tools/glob.md
  • 工具完整实现:packages/coding-agent/src/tools/glob.ts
  • 模式与路径解析:packages/coding-agent/src/tools/path-utils.ts
  • 原生匹配器:crates/pi-natives/src/glob_util.rs
  • 行为测试:packages/coding-agent/test/tools/glob.test.ts

【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询