From 69af696016a3c1c3c6aee42aeae8170871249c49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Aug 2026 08:53:44 +0000 Subject: [PATCH 1/4] Initial plan From 43737da864d3ec578b2482e54902628e53326f1c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Aug 2026 08:57:04 +0000 Subject: [PATCH 2/4] Honor ignore rules outside git repos during indexing Co-authored-by: Anandb71 <169837340+Anandb71@users.noreply.github.com> --- crates/arbor-watcher/src/indexer.rs | 156 +++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 1 deletion(-) diff --git a/crates/arbor-watcher/src/indexer.rs b/crates/arbor-watcher/src/indexer.rs index 716523f..9e04093 100644 --- a/crates/arbor-watcher/src/indexer.rs +++ b/crates/arbor-watcher/src/indexer.rs @@ -5,8 +5,10 @@ use arbor_core::{parse_file, CodeNode}; use arbor_graph::{ArborGraph, GraphBuilder, GraphStore}; +use ignore::gitignore::{Gitignore, GitignoreBuilder}; use ignore::WalkBuilder; use rayon::prelude::*; +use serde::Deserialize; use std::collections::HashSet; use std::path::{Path, PathBuf}; use std::time::Instant; @@ -44,6 +46,77 @@ pub struct IndexOptions { pub cache_path: Option, } +const DEFAULT_EXCLUDE_PATTERNS: &[&str] = &[ + "node_modules/", + "venv/", + ".venv/", + "site-packages/", + "__pycache__/", + "target/", + "dist/", + "build/", + "out/", +]; + +#[derive(Debug, Deserialize)] +struct ArborConfig { + #[serde(default)] + ignore: Vec, +} + +fn build_ignore_matcher(root: &Path) -> Option { + let mut builder = GitignoreBuilder::new(root); + for pattern in DEFAULT_EXCLUDE_PATTERNS { + if let Err(e) = builder.add_line(None, pattern) { + warn!("Invalid built-in ignore pattern '{}': {}", pattern, e); + } + } + + let config_path = root.join(".arbor").join("config.json"); + if config_path.exists() { + match std::fs::read_to_string(&config_path) { + Ok(text) => match serde_json::from_str::(&text) { + Ok(config) => { + for pattern in config.ignore { + let trimmed = pattern.trim(); + if trimmed.is_empty() { + continue; + } + if let Err(e) = builder.add_line(None, trimmed) { + warn!( + "Invalid ignore pattern '{}' in {}: {}", + trimmed, + config_path.display(), + e + ); + } + } + } + Err(e) => { + warn!("Failed to parse {}: {}", config_path.display(), e); + } + }, + Err(e) => { + warn!("Failed to read {}: {}", config_path.display(), e); + } + } + } + + match builder.build() { + Ok(matcher) => Some(matcher), + Err(e) => { + warn!("Failed to build ignore matcher: {}", e); + None + } + } +} + +fn is_ignored(path: &Path, is_dir: bool, matcher: Option<&Gitignore>) -> bool { + matcher + .map(|m| m.matched_path_or_any_parents(path, is_dir).is_ignore()) + .unwrap_or(false) +} + /// Indexes a directory and returns the code graph. /// /// This walks all source files, parses them, and builds the @@ -84,12 +157,16 @@ pub fn index_directory(root: &Path, options: IndexOptions) -> Result