Summary
When contextplus is rooted at a workspace whose .gitignore excludes the
sub-trees containing the actual source (e.g. monorepos with git-managed
sub-repos under repos/ or packages/, or any layout using nested git
repos / git submodules / local symlinks), those sub-trees are invisible
to the indexer. There's no way to override this short of editing the
parent .gitignore.
Repro
workspace/
├── .gitignore # contains: repos/
├── docs/ # indexed
└── repos/
├── lib-a/ # own git repo — NOT indexed
│ └── src/...
└── lib-b/ # own git repo — NOT indexed
└── src/...
bunx contextplus /path/to/workspace indexes only docs/. The
embeddings-cache.json contains zero entries from repos/lib-a/ or
repos/lib-b/, even though each sub-repo has its own .gitignore that
would happily include its src/.
Cause
src/core/walker.ts:
- L44 —
loadIgnoreRules reads .gitignore only at rootDir. Nested
.gitignore files (per-repo, per-package) are never loaded, so the
ignore matcher uses only the parent's rules.
- L63 —
entry.name.startsWith(".") skips every dotfile/dir
unconditionally, including any nested .git markers that might
otherwise signal "this is a sub-repo, treat its own ignore rules".
- No mechanism for multiple roots, include-overrides, or submodule
awareness in WalkOptions.
Why the obvious workarounds don't work
- Un-ignore the sub-trees in the parent
.gitignore — changes the
parent repo's git behavior (sub-repo contents start showing up in
git status, gitlinks vs. embedded trees confusion, etc.). Also
exposes build artifacts that the sub-repos correctly ignore but the
parent doesn't know about.
- Run a separate contextplus per sub-repo — works, but defeats
cross-repo tools (get_blast_radius, semantic_navigate,
get_feature_hub) at the workspace level. You can't ask "where is
X used across all my packages" from one place.
Suggested resolutions (any one would solve it)
- Walk nested
.gitignore files. When recursing into a directory,
load its .gitignore (if any) and merge with the inherited rules.
This is what ripgrep does and matches user mental model.
- Treat nested
.git as a sub-repo boundary: when walkRecursive
sees .git/ in a subdirectory, swap to that subdirectory's ignore
rules instead of inheriting the parent's. (Closer to native git
submodule semantics.)
- Add an
include / extraRoots option to WalkOptions — an
allow-list of paths that bypass parent ignore rules. Easiest to ship,
most explicit, doesn't change defaults.
Happy to send a PR for option (1) or (3) if you have a preference.
Environment
- contextplus 1.0.8
- Node via bun (
bunx contextplus)
- macOS 25.1.0
Summary
When contextplus is rooted at a workspace whose
.gitignoreexcludes thesub-trees containing the actual source (e.g. monorepos with git-managed
sub-repos under
repos/orpackages/, or any layout using nested gitrepos / git submodules / local symlinks), those sub-trees are invisible
to the indexer. There's no way to override this short of editing the
parent
.gitignore.Repro
bunx contextplus /path/to/workspaceindexes onlydocs/. Theembeddings-cache.jsoncontains zero entries fromrepos/lib-a/orrepos/lib-b/, even though each sub-repo has its own.gitignorethatwould happily include its
src/.Cause
src/core/walker.ts:loadIgnoreRulesreads.gitignoreonly atrootDir. Nested.gitignorefiles (per-repo, per-package) are never loaded, so theignorematcher uses only the parent's rules.entry.name.startsWith(".")skips every dotfile/dirunconditionally, including any nested
.gitmarkers that mightotherwise signal "this is a sub-repo, treat its own ignore rules".
awareness in
WalkOptions.Why the obvious workarounds don't work
.gitignore— changes theparent repo's git behavior (sub-repo contents start showing up in
git status, gitlinks vs. embedded trees confusion, etc.). Alsoexposes build artifacts that the sub-repos correctly ignore but the
parent doesn't know about.
cross-repo tools (
get_blast_radius,semantic_navigate,get_feature_hub) at the workspace level. You can't ask "where isX used across all my packages" from one place.
Suggested resolutions (any one would solve it)
.gitignorefiles. When recursing into a directory,load its
.gitignore(if any) and merge with the inherited rules.This is what
ripgrepdoes and matches user mental model..gitas a sub-repo boundary: whenwalkRecursivesees
.git/in a subdirectory, swap to that subdirectory's ignorerules instead of inheriting the parent's. (Closer to native git
submodule semantics.)
include/extraRootsoption toWalkOptions— anallow-list of paths that bypass parent ignore rules. Easiest to ship,
most explicit, doesn't change defaults.
Happy to send a PR for option (1) or (3) if you have a preference.
Environment
bunx contextplus)