Skip to content

fix(core): scope .boltignore to the repository root for nested searches - #94

Merged
DevFlex-AI merged 2 commits into
devfrom
boltignore-scope
Jul 31, 2026
Merged

fix(core): scope .boltignore to the repository root for nested searches#94
DevFlex-AI merged 2 commits into
devfrom
boltignore-scope

Conversation

@DevFlex-AI

@DevFlex-AI DevFlex-AI commented Jul 31, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #93

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Follow-up to #87, addressing the review findings that landed after merge. Ripgrep resolves --ignore-file patterns relative to its working directory, so a root .boltignore rule like /src/generated/ matched the wrong path whenever grep/glob/find ran from a subdirectory, and the child-to-root argument order gave parent files higher precedence than nearer ones.

.boltignore is now repository-root-only, and when a search starts below the root, rg runs from the root with the walk confined to the requested directory, so root-anchored rules keep their scope:

function boltignore(cwd: string) {
  const root = /* nearest ancestor with .git, else cwd */
  const file = path.join(root, ".boltignore")
  if (!fs.existsSync(file)) return { cwd, args: [], search: ".", prefix: "" }
  const search = path.relative(root, cwd)
  return {
    cwd: root,
    args: [`--ignore-file=${file}`],
    search: search || ".",
    prefix: search ? search.replaceAll("\\", "/") + "/" : "",
  }
}

User globs with a non-trailing slash are re-anchored onto the search directory (slash-less globs match basenames at any depth and need no translation), and output paths are re-based onto the requested cwd, so callers see the same relative paths as before.

In the HTTP file.list handler, a .boltignore read failure no longer silently becomes an empty policy: only NotFound yields an empty policy, anything else dies instead of exposing files the policy should hide. .boltignore rules are also added before .gitignore/.ignore so those keep higher precedence, mirroring rg's lowest-priority --ignore-file semantics.

How did you verify your code works?

  • Extended packages/core/test/ripgrep.test.ts with a root-anchored /src/generated/ rule checked from both the root and a subdirectory, for find and grep (3/3 pass)
  • bun typecheck in packages/core and packages/opencode

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
bolt-cli-app Ready Ready Preview Jul 31, 2026 4:19pm

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@DevFlex-AI, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c168b6b9-4144-4d80-b708-1f0fa61c0467

📥 Commits

Reviewing files that changed from the base of the PR and between 036264a and 23cbe53.

📒 Files selected for processing (3)
  • packages/core/src/ripgrep.ts
  • packages/core/test/ripgrep.test.ts
  • packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts

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

@github-actions

Copy link
Copy Markdown

The following comment was made by an LLM, it may be inaccurate:

@deepsource-io

deepsource-io Bot commented Jul 31, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 036264a...23cbe53 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade  

Focus Area: Reliability
Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Jul 31, 2026 4:18p.m. Review ↗
Shell Jul 31, 2026 4:18p.m. Review ↗
Secrets Jul 31, 2026 4:18p.m. Review ↗
Docker Jul 31, 2026 4:18p.m. Review ↗
Python Jul 31, 2026 4:18p.m. Review ↗
CSS Jul 31, 2026 4:18p.m. Review ↗
Rust Jul 31, 2026 4:18p.m. Review ↗
Ruby Jul 31, 2026 4:18p.m. Review ↗
Swift Jul 31, 2026 4:18p.m. Review ↗
PHP Jul 31, 2026 4:18p.m. Review ↗
Lua Jul 31, 2026 4:18p.m. Review ↗
Java Jul 31, 2026 4:18p.m. Review ↗
Go Jul 31, 2026 4:18p.m. Review ↗
C & C++ Jul 31, 2026 4:18p.m. Review ↗
Ansible Jul 31, 2026 4:18p.m. Review ↗
Apex Jul 31, 2026 4:18p.m. Review ↗
Elixir Jul 31, 2026 4:18p.m. Review ↗
Groovy Jul 31, 2026 4:18p.m. Review ↗
Objective-C Jul 31, 2026 4:18p.m. Review ↗
PowerShell Jul 31, 2026 4:18p.m. Review ↗
Terraform Jul 31, 2026 4:18p.m. Review ↗
VB.NET Jul 31, 2026 4:18p.m. Review ↗
SQL Jul 31, 2026 4:18p.m. Review ↗
Scala Jul 31, 2026 4:18p.m. Review ↗
Perl Jul 31, 2026 4:18p.m. Review ↗
Kotlin Jul 31, 2026 4:18p.m. Review ↗
Helm Jul 31, 2026 4:18p.m. Review ↗
Erlang Jul 31, 2026 4:18p.m. Review ↗
Dart Jul 31, 2026 4:18p.m. Review ↗
C# Jul 31, 2026 4:18p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

Comment on lines +31 to +50
function boltignore(cwd: string) {
const root = (() => {
let dir = cwd
while (true) {
if (fs.existsSync(path.join(dir, ".git"))) return dir
const parent = path.dirname(dir)
if (parent === dir) return cwd
dir = parent
}
})()
const file = path.join(root, ".boltignore")
if (!fs.existsSync(file)) return { cwd, args: [], search: ".", prefix: "" }
const search = path.relative(root, cwd)
return {
cwd: root,
args: [`--ignore-file=${file}`],
search: search || ".",
prefix: search ? search.replaceAll("\\", "/") + "/" : "",
}
return found
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

False positive: this is an ES module, so the function declaration is module-scoped, not global. Plain function declarations are the established style in this codebase.

Comment thread packages/core/src/ripgrep.ts Outdated
cwd: root,
args: [`--ignore-file=${file}`],
search: search || ".",
prefix: search ? search.replaceAll("\\", "/") + "/" : "",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unexpected string concatenation


In ES2015 (ES6), we can use template literals instead of string concatenation.

Comment on lines +56 to +62
function reroot(glob: string, prefix: string): string {
if (!prefix) return glob
if (glob.startsWith("!")) return "!" + reroot(glob.slice(1), prefix)
if (glob.startsWith("/")) return "/" + prefix + glob.slice(1)
if (glob.slice(0, -1).includes("/")) return prefix + glob
return glob
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

Comment thread packages/core/src/ripgrep.ts Outdated
// the search directory.
function reroot(glob: string, prefix: string): string {
if (!prefix) return glob
if (glob.startsWith("!")) return "!" + reroot(glob.slice(1), prefix)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unexpected string concatenation


In ES2015 (ES6), we can use template literals instead of string concatenation.

Comment thread packages/core/src/ripgrep.ts Outdated
function reroot(glob: string, prefix: string): string {
if (!prefix) return glob
if (glob.startsWith("!")) return "!" + reroot(glob.slice(1), prefix)
if (glob.startsWith("/")) return "/" + prefix + glob.slice(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unexpected string concatenation


In ES2015 (ES6), we can use template literals instead of string concatenation.

Comment on lines +66 to 70
function rebase(line: string, prefix: string) {
const normalized = line.replace(/^(?:\.[\\/])+/u, "").replaceAll("\\", "/")
const stripped = prefix && normalized.startsWith(prefix) ? normalized.slice(prefix.length) : normalized
return stripped.replace(/^\/+/u, "")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@DevFlex-AI
DevFlex-AI merged commit 550e405 into dev Jul 31, 2026
42 of 43 checks passed
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.

fix(core): .boltignore root-anchored patterns misresolve for subdirectory searches

1 participant