fix: improve glob tool Windows compatibility and rg resolution#309
Conversation
- Add OpenCode XDG data path (~/.local/share/opencode/bin/rg) to rg candidates - Enable auto-install for glob tool (uses resolveGrepCliWithAutoInstall) - Add Windows PowerShell Get-ChildItem fallback when rg is unavailable Fixes #307
Greptile SummaryFixed glob tool Windows/WSL compatibility by addressing ripgrep detection, auto-installation, and fallback command issues. Key Changes:
Resolution Order:
Testing: All 108 tests pass, typecheck succeeds, build completes successfully. Notes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Agent as AI Agent
participant GlobTool as glob tool
participant Resolver as resolveGrepCliWithAutoInstall
participant GetBundled as getOpenCodeBundledRg
participant Downloader as downloadAndInstallRipgrep
participant RunRg as runRgFiles
participant CLI as CLI (rg/find/powershell)
Agent->>GlobTool: execute({ pattern, path })
GlobTool->>Resolver: resolveGrepCliWithAutoInstall()
Resolver->>GetBundled: Check XDG data path first
Note over GetBundled: ~/.local/share/opencode/bin/rg
alt rg found
GetBundled-->>Resolver: { path: rgPath, backend: "rg" }
else rg not found
GetBundled-->>Resolver: null
Resolver->>Downloader: downloadAndInstallRipgrep()
Downloader-->>Resolver: rgPath
Resolver-->>GlobTool: { path: rgPath, backend: "rg" }
end
GlobTool->>RunRg: runRgFiles(options, resolvedCli)
alt backend === "rg"
RunRg->>CLI: spawn([rg, --files, --glob=pattern, paths...])
else platform === "win32"
RunRg->>CLI: spawn([powershell, Get-ChildItem...])
else Unix fallback
RunRg->>CLI: spawn([find, . -maxdepth N -name pattern])
end
CLI-->>RunRg: stdout (file paths)
RunRg-->>GlobTool: { files, totalFiles, truncated }
GlobTool-->>Agent: formatted result
|
| const escapedPath = searchPath.replace(/'/g, "''") | ||
| const escapedPattern = options.pattern.replace(/'/g, "''") |
There was a problem hiding this comment.
style: The escaping only handles single quotes by doubling them, but doesn't escape other PowerShell special characters like backticks, $, @, etc. For full injection protection, consider using PowerShell's -LiteralPath parameter for the path, which doesn't interpret wildcards or special characters.
| const escapedPath = searchPath.replace(/'/g, "''") | |
| const escapedPattern = options.pattern.replace(/'/g, "''") | |
| // Use -LiteralPath to avoid interpretation of special characters | |
| let psCommand = `Get-ChildItem -LiteralPath '${escapedPath}' -File -Recurse -Depth ${maxDepth - 1} -Filter '${escapedPattern}'` |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/tools/glob/cli.ts
Line: 54:55
Comment:
**style:** The escaping only handles single quotes by doubling them, but doesn't escape other PowerShell special characters like backticks, `$`, `@`, etc. For full injection protection, consider using PowerShell's `-LiteralPath` parameter for the path, which doesn't interpret wildcards or special characters.
```suggestion
// Use -LiteralPath to avoid interpretation of special characters
let psCommand = `Get-ChildItem -LiteralPath '${escapedPath}' -File -Recurse -Depth ${maxDepth - 1} -Filter '${escapedPattern}'`
```
How can I resolve this? If you propose a fix, please make it concise.| const paths = options.paths?.length ? options.paths : ["."] | ||
| const searchPath = paths[0] || "." |
There was a problem hiding this comment.
style: Only uses the first path when options.paths has multiple entries. Consider that PowerShell's -Path parameter can accept multiple paths (comma-separated).
| const paths = options.paths?.length ? options.paths : ["."] | |
| const searchPath = paths[0] || "." | |
| const paths = options.paths?.length ? options.paths : ["."] | |
| const searchPaths = paths.map(p => `'${p.replace(/'/g, "''")}'`).join(", ") |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/tools/glob/cli.ts
Line: 51:52
Comment:
**style:** Only uses the first path when `options.paths` has multiple entries. Consider that PowerShell's `-Path` parameter can accept multiple paths (comma-separated).
```suggestion
const paths = options.paths?.length ? options.paths : ["."]
const searchPaths = paths.map(p => `'${p.replace(/'/g, "''")}'`).join(", ")
```
How can I resolve this? If you propose a fix, please make it concise.…-yeongyu#309) Critical fixes: - Bug #1: SubagentStop hook defaulted all agents to 'failed' because SDK doesn't provide `success` field. Now defaults to 'completed' when undefined. - Bug code-yeongyu#4: Token stats lost across TokenTracker instances. Constructor now restores session stats from global state for the same session ID. - Bug code-yeongyu#5: Ultrawork session isolation bypassed when both session IDs were undefined (undefined === undefined). Now rejects all falsy session IDs. High priority fixes: - Bug code-yeongyu#6: Cancel skill force-clear missed 12+ state files (boulder, hud-state, subagent-tracking, checkpoints, etc). Added comprehensive list. - Bug code-yeongyu#7: HUD semverCompare() returned NaN on pre-release versions like "3.9.5-beta". Fixed to use parseInt and handle pre-release ordering. - Bug code-yeongyu#8: Silent JSON parse failures in critical state readers. Added error logging to ralph and ultrawork state readers. - Bug code-yeongyu#9: Stale task detection had no default behavior when onStaleSession callback was not configured. Now auto-cleans after 2x threshold. - Bug code-yeongyu#10: Hardcoded 3-architect assumption in validation. Extracted to REQUIRED_ARCHITECTS constant. Medium priority fixes: - Bug code-yeongyu#11: Auto-invoke history used non-atomic writes. Now uses atomicWriteJson to prevent corruption from concurrent sessions. - Bug code-yeongyu#12: Ecomode docs said "all tasks" use Haiku, contradicting the escalation paths. Clarified to "most tasks" with upgrade criteria. - Bug code-yeongyu#13: Added safeUnlinkSync/safeRmSync utilities to prevent ENOENT crashes during cleanup operations. - Bug code-yeongyu#14: State files containing user prompts written with 0644 permissions. Now writes with 0600 (owner-only read/write). - Bug code-yeongyu#15: Model names recorded inconsistently (e.g., 'claude-3-5-haiku' vs 'claude-haiku-4'). Now normalizes at recording time via exported normalizeModelName(). Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Summary
Fixes #307 - glob not working well on Windows/WSL
This PR addresses three issues affecting Windows users:
Wrong path detection for OpenCode's bundled rg - The glob tool wasn't finding ripgrep installed by OpenCode because it only checked paths relative to
process.execPath, not the XDG data directory where OpenCode actually installs it (~/.local/share/opencode/bin/rg)No auto-install capability - Unlike the grep tool, glob used synchronous
resolveGrepCli()which doesn't trigger auto-installation when rg is missingUnix
findfallback on Windows - When rg wasn't available, the glob tool fell back to Unixfindcommand with arguments like-maxdepth,-type fwhich are invalid on Windows (Windowsfind.exeis a completely different command for text search)Changes
src/tools/grep/constants.ts: Added OpenCode's XDG data path (~/.local/share/opencode/bin/rg) as highest priority candidate ingetOpenCodeBundledRg()src/tools/glob/constants.ts: ExportedresolveGrepCliWithAutoInstallfor use by glob toolsrc/tools/glob/cli.ts:buildPowerShellCommand()for Windows fallback usingGet-ChildItemrunRgFiles()to accept optional pre-resolved CLI and detect Windows platformsrc/tools/glob/tools.ts: Now usesresolveGrepCliWithAutoInstall()to ensure rg is auto-downloaded if missingResolution Order (for rg)
~/.local/share/opencode/bin/rg(new, highest priority)~/.cache/oh-my-opencode/bin/rgGet-ChildItemon Windows, Unixfindon other platformsTesting
bun run typecheck)bun test)bun run build)Notes
process.platformreturns"linux"in WSL, so it uses Unix commands-Depthparameter (PowerShell 5.0+, standard on Windows 10+)