Skip to content

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

Merged
A-x6 merged 2 commits into
devfrom
boltignore-scope
Jul 31, 2026
Merged

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

Conversation

@A-x6

@A-x6A-x6 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:

functionboltignore(cwd: string){constroot=/* nearest ancestor with .git, else cwd */constfile=path.join(root,".boltignore")if(!fs.existsSync(file))return{ cwd,args: [],search: ".",prefix: ""}constsearch=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

vercelBot commented Jul 31, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
bolt-cli-appReadyReadyPreviewJul 31, 2026 4:19pm

@coderabbitai

coderabbitaiBot 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-ioBot 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

AnalyzerStatusUpdated (UTC)Details
JavaScriptJul 31, 2026 4:18p.m.Review ↗
ShellJul 31, 2026 4:18p.m.Review ↗
SecretsJul 31, 2026 4:18p.m.Review ↗
DockerJul 31, 2026 4:18p.m.Review ↗
PythonJul 31, 2026 4:18p.m.Review ↗
CSSJul 31, 2026 4:18p.m.Review ↗
RustJul 31, 2026 4:18p.m.Review ↗
RubyJul 31, 2026 4:18p.m.Review ↗
SwiftJul 31, 2026 4:18p.m.Review ↗
PHPJul 31, 2026 4:18p.m.Review ↗
LuaJul 31, 2026 4:18p.m.Review ↗
JavaJul 31, 2026 4:18p.m.Review ↗
GoJul 31, 2026 4:18p.m.Review ↗
C & C++Jul 31, 2026 4:18p.m.Review ↗
AnsibleJul 31, 2026 4:18p.m.Review ↗
ApexJul 31, 2026 4:18p.m.Review ↗
ElixirJul 31, 2026 4:18p.m.Review ↗
GroovyJul 31, 2026 4:18p.m.Review ↗
Objective-CJul 31, 2026 4:18p.m.Review ↗
PowerShellJul 31, 2026 4:18p.m.Review ↗
TerraformJul 31, 2026 4:18p.m.Review ↗
VB.NETJul 31, 2026 4:18p.m.Review ↗
SQLJul 31, 2026 4:18p.m.Review ↗
ScalaJul 31, 2026 4:18p.m.Review ↗
PerlJul 31, 2026 4:18p.m.Review ↗
KotlinJul 31, 2026 4:18p.m.Review ↗
HelmJul 31, 2026 4:18p.m.Review ↗
ErlangJul 31, 2026 4:18p.m.Review ↗
DartJul 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 threadpackages/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 threadpackages/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 threadpackages/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>
@A-x6
A-x6 merged commit 550e405 into devJul 31, 2026
42 of 43 checks passed
Sign up for freeto 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

@A-x6