Skip to content

Add ripgrepBinaryPath setting + nix-darwin rg fallback paths (#3657) - #3703

Closed
psh4607 wants to merge 3 commits into
manaflow-ai:mainfrom
psh4607:fix/ripgrep-binary-path-3657
Closed

psh4607 wants to merge 3 commits into
manaflow-ai:mainfrom
psh4607:fix/ripgrep-binary-path-3657

Conversation

@psh4607

@psh4607 psh4607 commented May 7, 2026 •

Copy link
Copy Markdown
Contributor

Closes #3657.

Summary

cmux's project-search ("Find") was failing on Nix-managed macOS installs because rg lives in a nix profile path that wasn't in cmux's hardcoded fallback list, and Dock-launched macOS apps don't reliably inherit launchctl setenv PATH. Picked up after the reporter mentioned PR intent.

Changes

Two complementary fixes per the issue:

  1. New automation.ripgrepBinaryPath setting — mirrors the existing automation.claudeBinaryPath. Editable from Settings > Automation and from ~/.config/cmux/cmux.json. Highest-leverage fix for non-standard installs (Nix, asdf, custom locations).

  2. Extended hardcoded fallback list with common nix-darwin profile paths so single-install Nix users work out of the box without touching settings:

    • /etc/profiles/per-user/$USER/bin/rg
    • /run/current-system/sw/bin/rg
    • /nix/var/nix/profiles/default/bin/rg

Resolution order

RipgrepResolver.resolve() (new shared helper used by both FileExplorerSearchController and SessionIndexStore) checks in order:

  1. automation.ripgrepBinaryPath setting, if configured and executable
  2. Common install paths (Homebrew/MacPorts/system → nix-darwin profiles)
  3. \$PATH lookup (best-effort)

Existing Homebrew/MacPorts/system precedence is preserved so users with multiple installs aren't silently switched to a different rg. A configured-but-not-executable custom path logs and falls through to the fallback list rather than hard-failing — a stale typo'd setting shouldn't completely break Find when a valid binary still exists in a default location.

The resolver re-evaluates on every call (replacing SessionIndexStore.cachedRipgrepPath which was a static let that locked to first resolution), so a settings change takes effect immediately without an app restart.

Files

  • New: Sources/RipgrepResolver.swift — shared resolver + RipgrepIntegrationSettings accessors
  • New: cmuxTests/RipgrepResolverTests.swift — 8 unit tests covering custom path / fallback / env / nix path inclusion / Homebrew precedence / setting trim semantics
  • Modified: Sources/FileExplorerSearchController.swift, Sources/SessionIndexStore.swift — both call sites now delegate to RipgrepResolver.resolve()
  • Modified: Sources/cmuxApp.swift — new SettingsCard row, @AppStorage, reset binding
  • Modified: Sources/KeyboardShortcutSettingsFileStore.swift — JSON parser, supported settings paths, default template
  • Modified: Sources/SettingsNavigation.swift, Sources/SettingsSearchAliases.swift — settings discovery / search
  • Modified: web/data/cmux.schema.json — schema entry
  • Modified: Resources/Localizable.xcstrings — en/ja/ko translations for the 3 new UI strings

Verification

  • xcodebuild -scheme cmux-unit -only-testing:cmuxTests/RipgrepResolverTests — 8/8 passing locally (covers custom path priority, fallthrough on bad path, common-paths fallback, $PATH fallback, nil terminus, nix path inclusion, Homebrew precedence preservation, setting trim/empty handling)
  • ./scripts/reload.sh --tag fix-ripgrep-binary-path-3657 — Debug app builds clean
  • LSP diagnostics clean across all touched files
  • JSON schema and Localizable.xcstrings parse cleanly

Notes for reviewers

  • Per cmux test policy, no source-text/grep-style tests were added; unit tests verify observable runtime behavior of the resolver via injected customPath / commonPaths / environment / fileManager overrides.
  • Setting names match the issue's request exactly (automation.ripgrepBinaryPath).
  • Reporter is on macOS Apple Silicon + ripgrep 15.1.0 via nix-darwin; would be ideal to have them validate the merged build.

Summary by cubic

Adds a new automation.ripgrepBinaryPath setting and a shared RipgrepResolver with nix-darwin fallback paths so Find works on Nix-managed macOS (fixes #3657). Tightens resolution to accept only executable files, dedupes invalid-path logging, and re-resolves on each call.

  • New Features

    • Added automation.ripgrepBinaryPath (Settings > Automation and ~/.config/cmux/cmux.json), searchable via Settings (en/ja/ko aliases).
    • Introduced RipgrepResolver used by Find: setting → common paths (Homebrew/MacPorts/system → nix-darwin) → $PATH, re-evaluated on each call.
  • Bug Fixes

    • Rejects directories misidentified as executables; only returns a real executable file.
    • Invalid custom path now logs via os.Logger once per process and falls back to common locations/$PATH instead of failing.

Written for commit ffe8842. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added an Automation setting to specify a custom ripgrep (rg) binary path, editable in Settings and cleared by "Reset All Settings".
    • Settings search and navigation now include a "Ripgrep Binary Path" entry.
  • Behavior

    • Custom path takes effect immediately; app falls back to auto-detection when unset or invalid.
  • Localization

    • Added English, Japanese, Korean, and Ukrainian strings for the new setting and search aliases.
  • Tests

    • Added unit tests covering resolution precedence and settings behavior.

Copilot AI review requested due to automatic review settings May 7, 2026 13:37
@vercel

vercel Bot commented May 7, 2026

Copy link
Copy Markdown

@psh4607 is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps

greptile-apps Bot commented May 7, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds automation.ripgrepBinaryPath (Settings UI + JSON config + schema) and a shared RipgrepResolver that checks the user setting, a hardcoded fallback list extended with nix-darwin profile paths, then $PATH, re-evaluating on each call so changes take effect without a restart. Both FileExplorerSearchController and SessionIndexStore now delegate to the resolver instead of maintaining their own inline lookup logic.

  • RipgrepResolver.swift: new file-scoped resolver with nonisolated logger and dedup tracker, @unchecked Sendable NSLock guard, injected dependencies for testability, and 7-path fallback list covering Homebrew → MacPorts → system → three nix-darwin profiles.
  • SessionIndexStore: static let cachedRipgrepPath (resolved once at launch) replaced by a computed static var so the live setting is always honoured.
  • Settings plumbing: @AppStorage binding, SettingsCard row, JSON parser key, schema entry, settings search index, and en/ja/ko localization all added consistently alongside the existing claudeBinaryPath pattern.

Confidence Score: 5/5

Safe to merge — the resolver logic is straightforward, well-tested, and consistent with the existing Claude binary path pattern; no changes to search result correctness or data handling.

The change is well-scoped: a new resolver with injected dependencies (easily unit-tested), clean delegation at two call sites, and settings plumbing that mirrors a proven pattern. The one nit — resetInvalidPathLogTrackerForTesting() not wrapped in #if DEBUG — is a convention gap rather than a functional defect.

Sources/RipgrepResolver.swift — resetInvalidPathLogTrackerForTesting() should be wrapped in #if DEBUG to match the project convention used by CmuxEventBus and CmuxEventLogWriter.

Important Files Changed

Filename Overview
Sources/RipgrepResolver.swift New shared resolver — actor isolation, nonisolated declarations, and @unchecked Sendable lock all look correct; resetInvalidPathLogTrackerForTesting() is not #if DEBUG-guarded contrary to project convention
Sources/SessionIndexStore.swift Replaced one-shot static let cachedRipgrepPath with a computed static var delegating to RipgrepResolver.resolve() so settings changes take effect without restart; call-site change is minimal and correct
Sources/FileExplorerSearchController.swift Inline path-resolution logic replaced with a single RipgrepResolver.resolve() call; clean delegation, no functional change to surrounding logic
Sources/KeyboardShortcutSettingsFileStore.swift New ripgrepBinaryPath JSON key parsed and written to managedUserDefaults mirroring the existing claudeBinaryPath pattern exactly; default template entry added correctly
Sources/cmuxApp.swift New @AppStorage binding and SettingsCard row for ripgrepBinaryPath mirror the existing Claude binary path row; reset binding included correctly
cmuxTests/RipgrepResolverTests.swift 8 unit tests covering custom path priority, fallthrough on invalid path, common-paths fallback, PATH env fallback, nil terminus, directory rejection, Nix path inclusion, and Homebrew precedence; helper scaffolding is clean
web/data/cmux.schema.json Added ripgrepBinaryPath string entry with matching default and description, placed adjacent to claudeBinaryPath for consistency

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["ripgrepMatchingPaths() / ripgrepExecutable()"] --> B["RipgrepResolver.resolve()"]
    B --> C{"automation.ripgrepBinaryPath\nconfigured?"}
    C -- Yes --> D{"isExecutableRegularFile?"}
    D -- Yes --> E["return custom path"]
    D -- No --> F["log warning once\n(dedup tracker)"]
    F --> G["check commonPaths list"]
    C -- No --> G
    G -- found --> H["return first match\n(Homebrew → MacPorts → system → nix-darwin)"]
    G -- none found --> I{"PATH env lookup"}
    I -- found --> J["return PATH match"]
    I -- none found --> K["return nil\n(fall back to Foundation scan)"]
Loading

Reviews (4): Last reviewed commit: "review: nonisolated file-scope state + r..." | Re-trigger Greptile

Comment thread Sources/RipgrepResolver.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift
@coderabbitai

coderabbitai Bot commented May 7, 2026 •

Copy link
Copy Markdown

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a694a55a-63a1-4f73-8de1-416baa7db2b7

📥 Commits

Reviewing files that changed from the base of the PR and between 8e668ac and ffe8842.

📒 Files selected for processing (2)
  • Sources/RipgrepResolver.swift
  • cmuxTests/RipgrepResolverTests.swift

📝 Walkthrough

Walkthrough

This pull request adds ripgrep (rg) binary path resolution with user override capability. A new RipgrepResolver module implements multi-stage lookup (custom user path → common paths including nix-darwin profiles → $PATH), exposes it as an automation setting, wires it into search components and Settings UI, updates schema/localization and Xcode project entries, and adds unit tests.

Changes

Ripgrep Binary Path Resolution

Layer / File(s) Summary
Core Resolver API
Sources/RipgrepResolver.swift
RipgrepIntegrationSettings provides the UserDefaults key and custom path accessor with whitespace trimming. RipgrepResolver.resolve() performs multi-stage lookup: validates custom override, searches common paths (Homebrew, MacPorts, nix-darwin profiles by user), then scans $PATH directories.
Settings Schema & Persistence
Sources/KeyboardShortcutSettingsFileStore.swift, web/data/cmux.schema.json
Adds automation.ripgrepBinaryPath field to settings schema with empty-string default. Settings file store parses this field and maps it to RipgrepIntegrationSettings.customRipgrepPathKey in managed UserDefaults; included in default template.
Core Integration
Sources/SessionIndexStore.swift, Sources/FileExplorerSearchController.swift
SessionIndexStore uses a computed resolvedRipgrepPath that re-invokes RipgrepResolver.resolve() on access. FileExplorerSearchController.ripgrepExecutable() delegates discovery to the resolver instead of inline path scanning.
Settings UI & Navigation
Sources/cmuxApp.swift, Sources/SettingsNavigation.swift, Sources/SettingsSearchAliases.swift
SettingsView adds @AppStorage state and text field for custom ripgrep path in Automation section with reset support. SettingsNavigation indexes the setting with localized title/subtitle and anchor mapping. SettingsSearchAliases adds a related alias.
Localization & Build
Resources/Localizable.xcstrings, GhosttyTabs.xcodeproj/project.pbxproj
Adds ripgrep setting translations (title, placeholder, subtitle) and a search alias in English/Japanese/Korean. Xcode project registers RipgrepResolver.swift and RipgrepResolverTests.swift in project groups and includes them in the cmuxTests target build phases.
Tests
cmuxTests/RipgrepResolverTests.swift, cmuxTests/SettingsSearchIndexTests.swift
Adds tests validating resolver precedence, common-path ordering (Homebrew before per-user nix profile), $PATH fallback, trimming and normalization of custom path, and settings search/anchor coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • manaflow-ai/cmux#2655: Modifies settings file handling and allowed settings JSON paths in the same area of settings persistence.

🐰 "I hopped through nix profiles, Homebrew, and PATH,
I trimmed stray spaces and warned once per wrong path.
Now users can point rg where their system installs,
and rabbits find binaries without extra stalls."


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Cmux Swift @Concurrent ❓ Inconclusive No result was produced after verification. Marking as INCONCLUSIVE. Re-run the check or adjust instructions to produce a final result.
✅ Passed checks (12 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly summarizes the main changes: adding ripgrepBinaryPath setting and nix-darwin fallback paths for ripgrep resolution.
Description check ✅ Passed PR description comprehensively covers summary, changes, resolution order, verification, and files modified. Covers all key template sections except demo video (not applicable for settings/backend features).
Linked Issues check ✅ Passed All coding objectives from #3657 are met: new automation.ripgrepBinaryPath setting [#3657], nix-darwin fallback paths implemented [#3657], resolution order with fallthrough on invalid paths [#3657], and re-evaluation on each call [#3657].
Out of Scope Changes check ✅ Passed All changes directly support ripgrep binary resolution for Nix-managed macOS: resolver logic, settings UI, localization, search discoverability, JSON schema, and comprehensive tests. No unrelated modifications detected.
Cmux Swift Actor Isolation ✅ Passed No actor isolation violations. Utility enums use implicitly nonisolated static members. File-scoped Logger and tracker explicitly nonisolated. InvalidPathLogTracker safely uses NSLock with @unchecked.
Cmux Swift Blocking Runtime ✅ Passed No prohibited blocking introduced. NSLock in RipgrepResolver.swift for log deduping meets allowed exception: very small lock on non-async state, documented, microsecond duration.
Cmux No Hacky Sleeps ✅ Passed Check applies only to TypeScript, JavaScript, shell, and build/runtime scripts. PR modifies only Swift, JSON, Xcode project, and localization files. No in-scope files changed.
Cmux Swift Concurrency ✅ Passed PR introduces no legacy async patterns. New RipgrepResolver is pure synchronous with no Dispatch queues, completion handlers, Combine, or fire-and-forget Tasks. All integration uses synchronous calls.
Cmux Swift File And Package Boundaries ✅ Passed RipgrepResolver (125 lines) has single responsibility. File changes minimal/under threshold. No mixed responsibilities. Dependency-injectable. Suitable for app target.
Cmux Swift Logging ✅ Passed RipgrepResolver.swift uses os.Logger with nonisolated declaration, no print/NSLog, no secrets/PII, and deduplication. All ripgrep-related logging changes comply with swift-logging.md rules.
Cmux Swiftui State Layout ✅ Passed Adds customRipgrepPath via @AppStorage following existing pattern. No new ObservableObject/@published, no GeometryReader layout issues, no store refs in lazy containers, no render-time mutations.
Cmux Architecture Rethink ✅ Passed Clean path resolution with AppStorage setting and nix-darwin paths. No timing delays, async hacks, or lifecycle patterns. NSLock for log deduping is thread-safety, not an architectural anti-pattern.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses ripgrep (rg) discovery failures on Nix-managed macOS installs by introducing a user-configurable automation.ripgrepBinaryPath setting and centralizing rg path resolution (including nix-darwin fallback locations) for Find/search features.

Changes:

  • Add automation.ripgrepBinaryPath to settings UI/config/schema and implement a shared RipgrepResolver with nix-darwin fallback paths.
  • Update Find/search call sites (FileExplorerSearchController, SessionIndexStore) to use the shared resolver (and re-resolve after settings changes).
  • Includes additional unrelated changes: a new “Inherit Working Directory in New Workspaces” setting and a markdown cmd-click routing behavior change.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
web/data/cmux.schema.json Adds JSON schema entry for automation.ripgrepBinaryPath.
Sources/SettingsSearchAliases.swift Adds search alias for the new ripgrep setting (and an app setting).
Sources/SettingsNavigation.swift Adds settings-path anchor IDs (including ripgrep) and an app setting entry.
Sources/SessionIndexStore.swift Switches rg path resolution to the shared resolver and re-resolves per call.
Sources/RipgrepResolver.swift New shared resolver honoring custom path, common paths (incl. nix), then $PATH.
Sources/KeyboardShortcutSettingsFileStore.swift Adds parsing/template support for automation.ripgrepBinaryPath.
Sources/GhosttyTerminalView.swift Adjusts cmd-click markdown URL routing and defers split creation asynchronously.
Sources/FileExplorerSearchController.swift Delegates ripgrep executable discovery to RipgrepResolver.
Sources/cmuxApp.swift Adds Settings UI for ripgrep binary path (and also a workspace CWD inherit toggle).
Resources/Localizable.xcstrings Adds localized strings for new settings UI (and an app setting).
GhosttyTabs.xcodeproj/project.pbxproj Wires new source/test files into Xcode project build phases.
cmuxTests/RipgrepResolverTests.swift Adds unit tests covering resolver precedence and nix path inclusion.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/SettingsNavigation.swift
Comment thread Sources/SettingsSearchAliases.swift
Comment thread Sources/cmuxApp.swift Outdated
Comment thread Sources/SessionIndexStore.swift
Comment thread Sources/RipgrepResolver.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/SettingsNavigation.swift`:
- Line 420: The mapping "automation.ripgrepBinaryPath" references settingID(for:
.automation, idSuffix: "ripgrep-path") but there is no corresponding entry in
the settingEntries collection, so the new setting won't be discoverable by
search or navigation; fix it by adding a settingEntries entry with idSuffix:
"ripgrep-path" (matching the same anchor/title used for the UI/anchor for
ripgrep binary path) so the SettingsSearchIndex/anchor and the mapping stay
consistent — update the settingEntries array/object where other automation
entries live to include the ripgrep-path entry using the same identifier strings
referenced by settingID(for:idSuffix:).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 34f7fc3c-4734-41ce-b55a-4d0f6ef78244

📥 Commits

Reviewing files that changed from the base of the PR and between 7142e31 and e627a82.

📒 Files selected for processing (12)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Resources/Localizable.xcstrings
  • Sources/FileExplorerSearchController.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/RipgrepResolver.swift
  • Sources/SessionIndexStore.swift
  • Sources/SettingsNavigation.swift
  • Sources/SettingsSearchAliases.swift
  • Sources/cmuxApp.swift
  • cmuxTests/RipgrepResolverTests.swift
  • web/data/cmux.schema.json

Comment thread Sources/SettingsNavigation.swift
…w-ai#3657)

Find ('cmd-F') was failing on nix-darwin macOS installs because rg lives in a nix profile path that wasn't in cmux's hardcoded fallback list, and Dock-launched GUI apps don't reliably inherit launchctl setenv PATH.

Two complementary changes:

1. New automation.ripgrepBinaryPath setting (mirrors automation.claudeBinaryPath). Lets users on Nix, asdf, or other non-standard installs point cmux at their rg directly. Editable in Settings > Automation and via cmux.json.

2. Extended the fallback list to include common nix-darwin profile paths so single-install Nix users work out of the box: /etc/profiles/per-user/<user>/bin/rg, /run/current-system/sw/bin/rg, /nix/var/nix/profiles/default/bin/rg.

Resolution order is preserved: existing Homebrew/MacPorts/system precedence is kept, then nix paths, then $PATH lookup. The shared RipgrepResolver re-evaluates on each call so a settings change takes effect without an app restart.
@psh4607
psh4607 force-pushed the fix/ripgrep-binary-path-3657 branch from e627a82 to d4566c4 Compare May 7, 2026 14:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
Sources/SettingsNavigation.swift (1)

423-423: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the missing settingEntries item for ripgrep-path.

automation.ripgrepBinaryPath now maps to setting:automation:ripgrep-path, but there is still no matching setting(.automation, "ripgrep-path", ...) entry in settingEntries. This keeps the new setting non-discoverable/inconsistent in settings search/navigation metadata.

Proposed fix
         setting(.automation, "claude-code", String(localized: "settings.automation.claudeCode", defaultValue: "Claude Code Integration"), "agent hooks notifications"),
         setting(.automation, "claude-path", String(localized: "settings.automation.claudeCode.customPath", defaultValue: "Claude Binary Path"), "custom claude executable"),
+        setting(.automation, "ripgrep-path", String(localized: "settings.automation.ripgrep.customPath", defaultValue: "Ripgrep Binary Path"), "custom ripgrep rg executable nix"),
         setting(.automation, "cursor", String(localized: "settings.automation.cursor", defaultValue: "Cursor Integration"), "agent hooks notifications"),

Based on learnings: “when adding Settings navigation/search wiring, keep anchor and SettingsSearchIndex entries consistent to avoid broken jump-to behavior.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SettingsNavigation.swift` at line 423, The new mapping
"automation.ripgrepBinaryPath" was added but there is no corresponding entry in
settingEntries, so add a setting entry for ripgrep-path to the settingEntries
collection: create a setting(.automation, "ripgrep-path", ...) (matching the
same label/anchor used by settingID(for: .automation, idSuffix: "ripgrep-path"))
so the setting becomes discoverable and consistent with
SettingsSearchIndex/anchor logic; ensure the entry's identifier, display text,
and anchor match the existing pattern used by other automation entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/RipgrepResolver.swift`:
- Around line 53-55: The resolver currently accepts a candidate when
FileManager.isExecutableFile(atPath: customPath) is true, but that can be a
directory; update the validation to also check that customPath is not a
directory before returning it. Use FileManager.fileExists(atPath:isDirectory:)
(or URL.resourceValues with .isDirectoryKey) to detect directories and only
return customPath from the RipgrepResolver (the branch that calls
fileManager.isExecutableFile(atPath:)) when it is executable AND !isDirectory;
otherwise fall through to the existing fallback logic.

---

Duplicate comments:
In `@Sources/SettingsNavigation.swift`:
- Line 423: The new mapping "automation.ripgrepBinaryPath" was added but there
is no corresponding entry in settingEntries, so add a setting entry for
ripgrep-path to the settingEntries collection: create a setting(.automation,
"ripgrep-path", ...) (matching the same label/anchor used by settingID(for:
.automation, idSuffix: "ripgrep-path")) so the setting becomes discoverable and
consistent with SettingsSearchIndex/anchor logic; ensure the entry's identifier,
display text, and anchor match the existing pattern used by other automation
entries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 061c8d4a-e627-48c6-b2b9-59baec44057c

📥 Commits

Reviewing files that changed from the base of the PR and between e627a82 and d4566c4.

📒 Files selected for processing (11)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Resources/Localizable.xcstrings
  • Sources/FileExplorerSearchController.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/RipgrepResolver.swift
  • Sources/SessionIndexStore.swift
  • Sources/SettingsNavigation.swift
  • Sources/SettingsSearchAliases.swift
  • Sources/cmuxApp.swift
  • cmuxTests/RipgrepResolverTests.swift
  • web/data/cmux.schema.json

Comment thread Sources/RipgrepResolver.swift Outdated
Splits applied to Sisyphus's manaflow-ai#3703 review pass:

- SettingsNavigation: add missing settingEntries row for ripgrep-path so the new setting is discoverable via Settings search/navigation, not just the path-anchor lookup (CodeRabbit, Copilot).

- Localizable.xcstrings: add settings.search.alias.setting.automation.ripgrep-path with en/ja/ko translations so non-English locales get the localized search tokens (Copilot).

- RipgrepResolver: switch NSLog -> os.Logger (Greptile P1; project standard for diagnostic destinations) and dedupe the invalid-path warning per process so a stuck-misconfigured path can't spam the unified log on every search keystroke (Copilot).

- SessionIndexStore: rename cachedRipgrepPath -> resolvedRipgrepPath since it's now a re-resolving computed property, not a cache (Copilot).

- SettingsSearchIndexTests: add coverage for both the ripgrep alias-search path ('ripgrep nix', 'rg binary') and the path-anchor lookup so this discoverability gap can't silently regress.
@psh4607

psh4607 commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 8e668ac5 addressing the review pass (also rebased on origin/main so the PR is now scoped to automation.ripgrepBinaryPath only — the earlier diff accidentally bundled some unrelated WIP from a sibling branch, sorry about the noise).

Reviewer Comment Status
CodeRabbit (SettingsNavigation.swift L423) Missing settingEntries row → setting non-discoverable ✅ Added setting(.automation, "ripgrep-path", …) row + SettingsSearchIndexTests coverage so this can't silently regress
Copilot (SettingsNavigation.swift L424) Same as above ✅ Same fix
Copilot (SettingsSearchAliases.swift L87) Alias key missing in Localizable.xcstrings ✅ Added settings.search.alias.setting.automation.ripgrep-path with en/ja/ko translations
Greptile P1 + Copilot (RipgrepResolver.swift L62/L64) NSLog vs project's os.Logger; per-call log spam ✅ Switched to Logger(subsystem: "com.cmuxterm.app", category: "RipgrepResolver").warning(…) + per-process dedupe via InvalidPathLogTracker so each unique invalid path is warned at most once
Copilot (SessionIndexStore.swift L1215) cachedRipgrepPath name misleading ✅ Renamed to resolvedRipgrepPath (it's a re-resolving computed property, not a cache)
Copilot (cmuxApp.swift L5920) Unrelated app.workspaceInheritWorkingDirectory setting in this PR ✅ Removed via the rebase — was working-tree contamination from a sibling branch, never intended for this PR
Greptile (GhosttyTerminalView.swift L3855) Same — unrelated MarkdownPanelView change ✅ Removed via the rebase

PR is now clean: 11 files, 1 fix commit + 1 review-fix commit, scoped to ripgrep only.

Comment thread Sources/RipgrepResolver.swift Outdated
Comment thread Sources/RipgrepResolver.swift Outdated
…flow-ai#3657)

Addresses Greptile P1 + CodeRabbit feedback on the second review pass:

- nonisolated private let on the file-scoped Logger and InvalidPathLogTracker (project rule .github/review-bot-rules/swift-logging.md). Without it, RipgrepResolver.resolve() — a nonisolated synchronous function — would cross the MainActor boundary on every read once strict concurrency is fully enabled.

- Reject directory matches in the resolution path. FileManager.isExecutableFile(atPath:) is access(X_OK), so it returns true for any directory with the search/execute bit set. Filter via fileExists(atPath:isDirectory:) so a misconfigured automation.ripgrepBinaryPath pointing at a directory falls through to the real binary instead of being accepted.

- Regression test (testResolveRejectsDirectoryWithExecuteBit) locks in the directory-rejection behavior and documents the underlying Foundation quirk so it can't silently regress.

Note on subsystem string: Greptile suggested Logging.subsystem, but that constant doesn't exist in the codebase yet — every Logger in cmux today uses a hardcoded subsystem string. Matching the existing CmuxEventLogWriter pattern ("com.cmuxterm.app") to avoid scope creep; introducing a Logging enum is a separate refactor.
@psh4607

psh4607 commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Second review-fix pass pushed as ffe88420:

Reviewer Comment Fix
CodeRabbit (RipgrepResolver.swift L74) FileManager.isExecutableFile returns true for directories with x bit ✅ Added isExecutableRegularFile helper that pairs fileExists(atPath:isDirectory:) + isExecutableFile. Regression test testResolveRejectsDirectoryWithExecuteBit locks it in.
Greptile P1 (RipgrepResolver.swift L21) nonisolated private let missing on file-scoped Logger ✅ Added nonisolated
Greptile P1 (RipgrepResolver.swift L43) Same for ripgrepInvalidPathTracker ✅ Added nonisolated

Note on Logging.subsystem: Greptile suggested it but the constant doesn't exist in this codebase yet — every Logger in cmux today uses a hardcoded subsystem string (e.g. CmuxEventLogWriter uses "com.cmuxterm.app"). Matched that pattern to keep this PR scoped; introducing a Logging namespace enum is a separate cleanup that should land in its own PR.

Resolved 10 review threads from this and the previous pass. PR is now 3 commits, all addressed:

  • d4566c49 Add ripgrepBinaryPath setting + nix-darwin rg fallback paths
  • 8e668ac5 review: address ripgrep PR feedback (settingEntries, xcstrings alias, os.Logger + dedupe, rename)
  • ffe88420 review: nonisolated file-scope state + reject directory matches

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
Sources/RipgrepResolver.swift (1)

71-74: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard executable checks against directory paths.

FileManager.isExecutableFile(atPath:) may accept directories, so the resolver can return a directory as rg. That leads to launch failures and skips valid fallback binaries.

Suggested patch
 enum RipgrepResolver {
+    private static func isExecutableFilePath(_ path: String, fileManager: FileManager) -> Bool {
+        var isDirectory: ObjCBool = false
+        guard fileManager.fileExists(atPath: path, isDirectory: &isDirectory),
+              !isDirectory.boolValue else {
+            return false
+        }
+        return fileManager.isExecutableFile(atPath: path)
+    }
+
     static func resolve(
         customPath: String? = RipgrepIntegrationSettings.customRipgrepPath(),
         commonPaths: [String] = RipgrepResolver.defaultCommonPaths(),
         environment: [String: String] = ProcessInfo.processInfo.environment,
         fileManager: FileManager = .default
     ) -> String? {
         if let customPath {
-            if fileManager.isExecutableFile(atPath: customPath) {
+            if isExecutableFilePath(customPath, fileManager: fileManager) {
                 return customPath
             }
             // Configured-but-not-executable falls through to the common paths
             // so a stale/typo'd setting doesn't completely disable Find when a
             // valid binary still exists in a default location.
@@
-        for path in commonPaths where fileManager.isExecutableFile(atPath: path) {
+        for path in commonPaths where isExecutableFilePath(path, fileManager: fileManager) {
             return path
         }
@@
-                if fileManager.isExecutableFile(atPath: candidate) {
+                if isExecutableFilePath(candidate, fileManager: fileManager) {
                     return candidate
                 }
             }
         }
         return nil
In Apple Foundation, can FileManager.isExecutableFile(atPath:) return true for directories, and is pairing it with fileExists(atPath:isDirectory:) the recommended way to validate an executable file path?

Also applies to: 84-85

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/RipgrepResolver.swift` around lines 71 - 74, The current check using
fileManager.isExecutableFile(atPath: customPath) can return true for
directories; update the customPath and fallback checks to also call
fileManager.fileExists(atPath:isDirectory:) (using an
UnsafeMutablePointer<ObjCBool> or inout Bool) to ensure the path exists AND
isDirectory is false before returning it—i.e., require both
isExecutableFile(atPath:) == true and isDirectory == false for customPath and
the other executable-path checks in RipgrepResolver (the blocks around
customPath and the fallback checks at the lines referenced).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@Sources/RipgrepResolver.swift`:
- Around line 71-74: The current check using
fileManager.isExecutableFile(atPath: customPath) can return true for
directories; update the customPath and fallback checks to also call
fileManager.fileExists(atPath:isDirectory:) (using an
UnsafeMutablePointer<ObjCBool> or inout Bool) to ensure the path exists AND
isDirectory is false before returning it—i.e., require both
isExecutableFile(atPath:) == true and isDirectory == false for customPath and
the other executable-path checks in RipgrepResolver (the blocks around
customPath and the fallback checks at the lines referenced).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 60ec2599-2c89-45e1-a1a2-26826d603b1f

📥 Commits

Reviewing files that changed from the base of the PR and between d4566c4 and 8e668ac.

📒 Files selected for processing (5)
  • Resources/Localizable.xcstrings
  • Sources/RipgrepResolver.swift
  • Sources/SessionIndexStore.swift
  • Sources/SettingsNavigation.swift
  • cmuxTests/SettingsSearchIndexTests.swift

@psh4607 psh4607 closed this May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ripgrepBinaryPath setting + extend rg search to nix profile paths

2 participants