Summary
当一个 workspace 是大型聚合目录——本身不是 git 仓库,内部包含多个独立 git 仓库(例如 vllm 主仓库 + 若干插件/关联仓库,统一在一个 workspace 下开发):
workspace/ ← 非 git 仓库(聚合根)
├── git仓库1/ ← 独立 repo(如 vllm)
├── git仓库2/ ← 独立 repo(如 vllm-engine 插件)
└── git仓库3/ ← 独立 repo(如推理插件)
此时 Flashgrep 索引搜索两条路都走不通:
- 以整个 workspace 为 repo 打开:flashgrep 的索引模型依赖 git 基线(
base-snapshot 绑定 commit),而聚合根不是 git 仓库 → 无法建立基线 → 索引不可用(Grep 退化为全量扫描,Glob 无 fallback 直接受影响)。
- 进入某个子仓库打开:搜索 scope 被强制限定在该 repo 内(越界直接报 "Search path is outside workspace root")→ 搜不到同 workspace 下其他关联仓库 → 丢失跨仓库搜索能力。
根因是 flashgrep 的集成模型是单仓库的(一次搜索请求只带一个 repo_id,scope 必须落在该 repo 内),而产品需要的是多仓库聚合 workspace 场景。
- 类型: Bug / 能力缺失(对多 git 子仓库 workspace 布局的支持)
- 严重度: 中~高(影响 monorepo/多仓库布局用户的 Grep/Glob,表现为搜不到或降级)
- 影响面: 桌面端 Glob/Grep 工具(本地 workspace),多 git 子仓库布局用户
Area
Desktop app
Reproduction or evidence
复现步骤:
- 建立如上目录结构:聚合根
workspace/ 不是 git 仓库,内部 git仓库1/、git仓库2/ 各自 git init。
- 开启
app.ai_experience.enable_workspace_search,以 workspace/ 为工作目录打开。
- 调用 Grep/Glob 搜索跨仓库内容(如同时分布在两个子仓库的符号)。
- 观察:
- 以 workspace 根为 repo 打开时,索引停留在
MissingBaseSnapshot/扫描降级(Grep 慢或结果不完整;Glob 依赖索引目录,行为异常,与 #TODO 关联的超时问题互相放大)。
- 把 workspace 切到
git仓库1/ 打开时,能搜到仓库 1 内部,但搜索 git仓库2/ 路径直接报越界/无结果——丢失关联仓库权限。
代码证据(单仓库模型的绑定点)
| 位置 |
事实 |
flashgrep/protocol.rs:105-119 |
SearchParams { repo_id, ... }、GlobParams { repo_id, scope }——一次搜索请求只携带单个 repo_id,无跨仓库聚合字段 |
flashgrep/protocol.rs:357-374 |
RepoStatus 的索引状态是单仓库语义:base_snapshot_root / snapshot_key / dirty_files{modified,deleted,new} / rebuild_recommended——基线绑定 git commit |
flashgrep/protocol.rs:378-386 |
RepoPhase::MissingBaseSnapshot——无可用 git 基线时索引不可用,是显式状态 |
workspace_search/service.rs:78,435 |
sessions: HashMap<PathBuf, SessionEntry>、get_or_open_session(repo_root)——session 以单个 repo_root 为键,一次搜索只打开/查询一个 repo |
workspace_search/service.rs:906-921 |
normalize_scope_path:normalized.starts_with(repo_root) 否则报 "Search path is outside workspace root"——scope 硬性限定在单 repo 内,跨仓库路径被拒绝 |
workspace_search/service.rs:226 |
Grep 搜索带 with_scan_fallback(true)——可降级扫描;GlobParams(protocol.rs:115-119)无 fallback 字段,Glob 无降级路径 |
grep_tool.rs:270-274 |
工具层把 context.workspace.root_path_string() 整体当作 repo_root 传参——整个 workspace 被当成一个 repo,多子仓库结构被压平 |
glob_tool.rs:419-426 |
GlobSearchRequest { repo_root: workspace_root, ... }——同上,整个 workspace 当单个 repo |
flashgrep/rpc_client.rs:340 |
daemon 能力声明 "workspace_list": false——daemon 不支持列出 workspace 内多仓库并聚合 |
resources/flashgrep/README.md:5 |
daemon 为外部项目 wgqqqqq/flashgrep v0.2.10,BitFun 侧为集成方;多仓库能力需在集成层解决 |
根因调用链
工具层 Grep/Glob
→ repo_root = workspace 聚合根(非 git 仓库)
→ WorkspaceSearchService::search_content/glob
→ get_or_open_session(workspace_root) // 单 repo session
→ normalize_scope_path:scope 必须 starts_with(repo_root) // 跨子仓库路径被拒
→ daemon:无 git 基线 → MissingBaseSnapshot → 索引不可用/降级
两个方向的矛盾:
- 把 workspace 根当 repo:flashgrep 索引模型要求 git 基线(base-snapshot 绑定 commit、dirty 分类来自 git status、语料遵循 .gitignore),聚合根不是 git 仓库时基线缺失;即便强行索引,也会重复扫描内部子仓库并撞上子仓库各自的 .gitignore 边界。
- 把子仓库当 repo:scope 被
starts_with(repo_root) 强制锁死在单仓库内,同 workspace 的其他关联仓库(插件等)不可搜索——正是「进入某个仓库就失去外部其他关联仓库的权限」。
工具层没有任何跨仓库聚合逻辑:不枚举 workspace 下的子仓库、不开多个 repo session 并发查询、不合并结果。一次 Grep/Glob 只能命中一个 repo_root。
Expected behavior
多 git 子仓库的聚合 workspace 应被当作仓库集合处理,而不是单个 repo:
- 仓库集合抽象:workspace 是多个 git 仓库的组合。搜索请求支持
repo_ids: Vec<String>(或聚合根 + 子仓库发现),daemon 或服务层按子仓库建索引并聚合结果,去重后返回。
- 子仓库自动发现:服务层识别 workspace 内的 git 子仓库边界(
git rev-parse --show-toplevel 或 .git 目录发现),每个子仓库一个 repo session;跨仓库 Grep/Glob 并发查询各 session 后合并。
- scope 语义扩展:搜索路径允许跨多个子仓库,越界检查从「单 repo 内」放宽为「任一子仓库内」,并保留权限边界(子仓库外仍拒绝)。
- 无 git 基线的降级路径:聚合根非 git 时,Grep 的 scan fallback 需显式告知用户「索引不可用、正在全量扫描」;Glob 需要补齐 fallback(当前无)。
- 避免重复扫描:子仓库边界识别后,聚合根扫描应跳过嵌套
.git 目录与子仓库内部,防止重复索引。
建议方案
- 短期(集成层):
WorkspaceSearchService 增加 workspace → 子仓库集合的映射;Grep/Glob 工具对跨仓库请求并发调用各子仓库 session 并合并结果;scope 越界检查按仓库集合判定。
- 中期(协议层):与外部
wgqqqqq/flashgrep 上游对齐多仓库能力(workspace_list、多 repo 搜索参数),或引入聚合根索引模式(非 git 目录也可建不依赖 commit 的文件快照基线)。
- 配套:与本仓库 #TODO(Flashgrep RPC 调用链无超时)联动——多仓库并发查询放大了 daemon 单请求挂起的影响,修复时须一并加上超时与降级。
Environment, if relevant
BitFun commit: 4d3869859 (2026-08-14)
OS: Windows x64
FLASHGREP_DAEMON_BIN: C:\Users\wfwt\AppData\Local\BitFun\flashgrep\flashgrep-x86_64-pc-windows-msvc.exe
flashgrep daemon: wgqqqqq/flashgrep v0.2.10(外部项目,本仓库集成)
布局: workspace 聚合根(非 git)+ 多个独立 git 子仓库(主项目 + 插件)
Related files
src/crates/services/services-integrations/src/workspace_search/flashgrep/protocol.rs
src/crates/services/services-integrations/src/workspace_search/service.rs
src/crates/services/services-integrations/src/workspace_search/flashgrep/client.rs
src/crates/services/services-integrations/src/workspace_search/flashgrep/rpc_client.rs
src/crates/assembly/core/src/agentic/tools/implementations/grep_tool.rs
src/crates/assembly/core/src/agentic/tools/implementations/glob_tool.rs
src/crates/assembly/core/src/service/search/service.rs
resources/flashgrep/README.md
Summary
当一个 workspace 是大型聚合目录——本身不是 git 仓库,内部包含多个独立 git 仓库(例如 vllm 主仓库 + 若干插件/关联仓库,统一在一个 workspace 下开发):
此时 Flashgrep 索引搜索两条路都走不通:
base-snapshot绑定 commit),而聚合根不是 git 仓库 → 无法建立基线 → 索引不可用(Grep 退化为全量扫描,Glob 无 fallback 直接受影响)。根因是 flashgrep 的集成模型是单仓库的(一次搜索请求只带一个
repo_id,scope 必须落在该 repo 内),而产品需要的是多仓库聚合 workspace 场景。Area
Desktop app
Reproduction or evidence
复现步骤:
workspace/不是 git 仓库,内部git仓库1/、git仓库2/各自git init。app.ai_experience.enable_workspace_search,以workspace/为工作目录打开。MissingBaseSnapshot/扫描降级(Grep 慢或结果不完整;Glob 依赖索引目录,行为异常,与 #TODO 关联的超时问题互相放大)。git仓库1/打开时,能搜到仓库 1 内部,但搜索git仓库2/路径直接报越界/无结果——丢失关联仓库权限。代码证据(单仓库模型的绑定点)
flashgrep/protocol.rs:105-119SearchParams { repo_id, ... }、GlobParams { repo_id, scope }——一次搜索请求只携带单个repo_id,无跨仓库聚合字段flashgrep/protocol.rs:357-374RepoStatus的索引状态是单仓库语义:base_snapshot_root/snapshot_key/dirty_files{modified,deleted,new}/rebuild_recommended——基线绑定 git commitflashgrep/protocol.rs:378-386RepoPhase::MissingBaseSnapshot——无可用 git 基线时索引不可用,是显式状态workspace_search/service.rs:78,435sessions: HashMap<PathBuf, SessionEntry>、get_or_open_session(repo_root)——session 以单个 repo_root 为键,一次搜索只打开/查询一个 repoworkspace_search/service.rs:906-921normalize_scope_path:normalized.starts_with(repo_root)否则报 "Search path is outside workspace root"——scope 硬性限定在单 repo 内,跨仓库路径被拒绝workspace_search/service.rs:226with_scan_fallback(true)——可降级扫描;GlobParams(protocol.rs:115-119)无 fallback 字段,Glob 无降级路径grep_tool.rs:270-274context.workspace.root_path_string()整体当作 repo_root 传参——整个 workspace 被当成一个 repo,多子仓库结构被压平glob_tool.rs:419-426GlobSearchRequest { repo_root: workspace_root, ... }——同上,整个 workspace 当单个 repoflashgrep/rpc_client.rs:340"workspace_list": false——daemon 不支持列出 workspace 内多仓库并聚合resources/flashgrep/README.md:5wgqqqqq/flashgrepv0.2.10,BitFun 侧为集成方;多仓库能力需在集成层解决根因调用链
两个方向的矛盾:
starts_with(repo_root)强制锁死在单仓库内,同 workspace 的其他关联仓库(插件等)不可搜索——正是「进入某个仓库就失去外部其他关联仓库的权限」。工具层没有任何跨仓库聚合逻辑:不枚举 workspace 下的子仓库、不开多个 repo session 并发查询、不合并结果。一次 Grep/Glob 只能命中一个 repo_root。
Expected behavior
多 git 子仓库的聚合 workspace 应被当作仓库集合处理,而不是单个 repo:
repo_ids: Vec<String>(或聚合根 + 子仓库发现),daemon 或服务层按子仓库建索引并聚合结果,去重后返回。git rev-parse --show-toplevel或.git目录发现),每个子仓库一个 repo session;跨仓库 Grep/Glob 并发查询各 session 后合并。.git目录与子仓库内部,防止重复索引。建议方案
WorkspaceSearchService增加 workspace → 子仓库集合的映射;Grep/Glob 工具对跨仓库请求并发调用各子仓库 session 并合并结果;scope 越界检查按仓库集合判定。wgqqqqq/flashgrep上游对齐多仓库能力(workspace_list、多 repo 搜索参数),或引入聚合根索引模式(非 git 目录也可建不依赖 commit 的文件快照基线)。Environment, if relevant
Related files
src/crates/services/services-integrations/src/workspace_search/flashgrep/protocol.rssrc/crates/services/services-integrations/src/workspace_search/service.rssrc/crates/services/services-integrations/src/workspace_search/flashgrep/client.rssrc/crates/services/services-integrations/src/workspace_search/flashgrep/rpc_client.rssrc/crates/assembly/core/src/agentic/tools/implementations/grep_tool.rssrc/crates/assembly/core/src/agentic/tools/implementations/glob_tool.rssrc/crates/assembly/core/src/service/search/service.rsresources/flashgrep/README.md