Skip to main content

vtcode_commons/
exclusions.rs

1//! Centralized exclusion constants and helpers for file traversal.
2//!
3//! All directory walkers, grep invocations, and file-operation tools should
4//! reference these constants instead of maintaining their own skip lists.
5
6/// Name of the workspace-local ignore file that mirrors `.gitignore` semantics
7/// but only affects VT Code's own file operations.
8///
9/// Registered as a custom ignore filename on every traversal walker so the
10/// same patterns prune the file picker, grep/list tools, and the indexer.
11pub const VTCODE_IGNORE_FILE: &str = ".vtcodegitignore";
12
13/// Directories skipped by default during workspace traversal.
14///
15/// This covers build artifacts, dependency stores, VCS metadata, and IDE
16/// configuration directories that are almost never relevant to code search
17/// or analysis.
18pub const DEFAULT_EXCLUDED_DIRS: &[&str] = &[
19    ".git",
20    "node_modules",
21    "target",
22    "dist",
23    ".next",
24    "vendor",
25    ".cursor",
26    ".vtcode",
27    ".vscode",
28    ".idea",
29];
30
31/// Sensitive files that must never be exposed in listings, search results,
32/// or the TUI file palette.  These contain secrets, credentials, or
33/// environment-specific configuration.
34pub const SENSITIVE_FILES: &[&str] = &[
35    ".env",
36    ".env.local",
37    ".env.production",
38    ".env.development",
39    ".env.test",
40    ".DS_Store",
41    ".git-credentials",
42    ".netrc",
43    ".npmrc",
44    ".pypirc",
45    "credentials",
46    "credentials.json",
47    "id_dsa",
48    "id_ecdsa",
49    "id_ed25519",
50    "id_rsa",
51];
52
53/// Glob patterns passed to ripgrep (or other search back-ends) to exclude
54/// noisy vendor/build directories from results.
55pub const DEFAULT_IGNORE_GLOBS: &[&str] = &[
56    "**/.git/**",
57    "**/node_modules/**",
58    "**/target/**",
59    "**/.cursor/**",
60    "**/dist/**",
61    "**/.next/**",
62    "**/vendor/**",
63    "**/.vtcode/**",
64    "**/.vscode/**",
65    "**/.idea/**",
66];
67
68/// Returns `true` if `name` matches any entry in [`SENSITIVE_FILES`] or
69/// starts with `.env.` (catches all dotenv variants). Matching is
70/// case-insensitive because macOS and Windows commonly use case-insensitive
71/// filesystems.
72pub fn is_sensitive_file(name: &str) -> bool {
73    SENSITIVE_FILES.iter().any(|sensitive| name.eq_ignore_ascii_case(sensitive))
74        || name.get(..5).is_some_and(|prefix| prefix.eq_ignore_ascii_case(".env."))
75}
76
77#[cfg(test)]
78mod tests {
79    use super::is_sensitive_file;
80
81    #[test]
82    fn sensitive_file_matching_is_case_insensitive() {
83        assert!(is_sensitive_file(".ENV"));
84        assert!(is_sensitive_file(".Env.Local"));
85        assert!(is_sensitive_file(".NPMRC"));
86        assert!(!is_sensitive_file(".environment"));
87    }
88
89    #[test]
90    fn ssh_private_key_basenames_are_sensitive() {
91        assert!(is_sensitive_file("id_dsa"));
92        assert!(is_sensitive_file("id_ecdsa"));
93        assert!(is_sensitive_file("ID_ECDSA"));
94        assert!(!is_sensitive_file("id_ecdsa.pub"));
95    }
96}