Skip to content

Commit 9826e26

Browse files
authored
feat: Auto-indexing in cache folder (#162)
1 parent 2fe3b53 commit 9826e26

29 files changed

Lines changed: 780 additions & 341 deletions

README.md

Lines changed: 82 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,7 @@ semble search "save_pretrained" ./my-project
6969
semble search "save model to disk" ./my-project --top-k 10
7070
​```
7171

72-
If you anticipate doing more than one search, use `semble index` to create an index.
73-
74-
​```bash
75-
semble index ./my-project -o my_index
76-
​```
77-
78-
You can then reuse this index later on:
79-
80-
​```bash
81-
semble search "save_pretrained" --index my_index
82-
​```
83-
84-
An index is not automatically updated, so if the code changes significantly, reindex. If you notice stale results while resolving searches to files, reindex.
72+
The index is built on first run (and cached for subsequent runs) and invalidated automatically when files change.
8573

8674
Use `--content docs` to search documentation and prose, `--content config` for config files (yaml, toml, etc.), or `--content all` to search code, docs, and config:
8775

@@ -97,20 +85,17 @@ Use `semble find-related` to discover code similar to a known location (pass `fi
9785
semble find-related src/auth.py 42 ./my-project
9886
​```
9987

100-
Like search, `find-related` also accepts an `--index` argument.
101-
10288
`path` defaults to the current directory when omitted; git URLs are accepted.
10389

10490
If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place.
10591

10692
### Workflow
10793

108-
1. Index the repo using `semble index -o cached_index`.
109-
2. Start with `semble search` to find relevant chunks. Pass the index to achieve results faster.
110-
3. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
111-
4. Inspect full files only when the returned chunk does not give enough context.
112-
5. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
113-
6. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.
94+
1. Start with `semble search` to find relevant chunks. The index is built and cached automatically.
95+
2. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
96+
3. Inspect full files only when the returned chunk does not give enough context.
97+
4. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
98+
5. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.
11499
```
115100

116101
</details>
@@ -330,7 +315,55 @@ Add to `~/.config/zed/settings.json` (or `.zed/settings.json` in your project):
330315
By default the MCP server indexes only code files. To also index documentation, config, or everything, append `--content docs`, `--content config`, or `--content all` to the server command, or a combination, e.g. `--content code docs`. For example, in Claude Code: `claude mcp add semble -s user -- uvx --from "semble[mcp]" semble --content all`.
331316

332317

333-
## Sub-agent setup
318+
<a id="bash-agentsmd"></a>
319+
320+
## Bash / AGENTS.md
321+
322+
An alternative to MCP is to invoke Semble via Bash. Sub-agents cannot call MCP tools directly, so this is the only option for sub-agent support; it can also be used alongside MCP for the top-level agent.
323+
324+
To add Bash support, append the following to your `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, or equivalent:
325+
326+
```markdown
327+
## Code Search
328+
329+
Use `semble search` to find code by describing what it does or naming a symbol/identifier, instead of grep:
330+
331+
​```bash
332+
semble search "authentication flow" ./my-project
333+
semble search "save_pretrained" ./my-project
334+
semble search "save model to disk" ./my-project --top-k 10
335+
​```
336+
337+
The index is built on first run (and cached for subsequent runs) and invalidated automatically when files change.
338+
339+
Use `--content docs` to search documentation and prose, `--content config` for config files (yaml, toml, etc.), or `--content all` to search code, docs, and config:
340+
341+
​```bash
342+
semble search "deployment guide" ./my-project --content docs
343+
semble search "database host port" ./my-project --content config
344+
semble search "authentication" ./my-project --content all
345+
​```
346+
347+
Use `semble find-related` to discover code similar to a known location (pass `file_path` and `line` from a prior search result):
348+
349+
​```bash
350+
semble find-related src/auth.py 42 ./my-project
351+
​```
352+
353+
`path` defaults to the current directory when omitted; git URLs are accepted.
354+
355+
If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place.
356+
357+
### Workflow
358+
359+
1. Start with `semble search` to find relevant chunks. The index is built and cached automatically.
360+
2. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
361+
3. Inspect full files only when the returned chunk does not give enough context.
362+
4. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
363+
5. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.
364+
```
365+
366+
### Sub-agent setup
334367

335368
Claude Code, Gemini CLI, Cursor, OpenCode, GitHub Copilot CLI, and Kiro all support a dedicated semble search sub-agent. Run `semble init` once in your project root:
336369

@@ -350,13 +383,9 @@ If semble is not on `$PATH`, prefix the command with `uvx --from "semble[mcp]"`.
350383
Semble also ships as a standalone CLI. This is useful in scripts or anywhere you want search results without an MCP session.
351384

352385
```bash
353-
# Search a local repo
386+
# Search a local repo (index is built and cached automatically)
354387
semble search "authentication flow" ./my-project
355388

356-
# Index first for faster repeated searches (--index works with any command below)
357-
semble index ./my-project -o my-index
358-
semble search "authentication flow" --index my-index
359-
360389
# Search a remote repo (cloned on demand)
361390
semble search "save model to disk" https://github.com/MinishLab/model2vec
362391

@@ -372,6 +401,31 @@ semble find-related src/auth.py 42 ./my-project
372401

373402
`--content` accepts `code` (default), `docs`, `config`, or `all`. `path` defaults to the current directory when omitted; git URLs are accepted. If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place.
374403

404+
<details>
405+
<summary>Controlling which files are indexed</summary>
406+
407+
Semble reads `.gitignore` and `.sembleignore` files to determine which files to index. Both files use standard gitignore syntax and their patterns are merged. `.sembleignore` lets you add semble-specific rules without touching `.gitignore`. Rules are applied recursively, so a `.sembleignore` in a subdirectory applies to that subtree.
408+
409+
**Excluding files:** add patterns the same way you would in `.gitignore`:
410+
411+
```
412+
# .sembleignore
413+
generated/ # exclude generated dir
414+
*.pb.go. # exclude Go protobuf files
415+
```
416+
417+
**Including non-default extensions:** prefix the extension pattern with `!` to force-include files that semble wouldn't index by default:
418+
419+
```
420+
# .sembleignore
421+
!*.proto # include Protobuf files
422+
!*.cob # include COBOL files
423+
```
424+
425+
Semble also always skips a set of well-known non-source directories regardless of ignore files (e.g. `node_modules/`, `.venv/`, `dist/`, `build/`, `__pycache__/`, and similar).
426+
427+
</details>
428+
375429
<details>
376430
<summary>Savings</summary>
377431

@@ -394,7 +448,7 @@ semble savings --verbose # also show breakdown by call type
394448

395449
Savings are calculated as follows: for each call, semble records the total character count of the unique files containing returned chunks and the character count of the snippets returned. Estimated tokens saved is `(file chars − snippet chars) / 4` (4 chars per token). This is a conservative estimate: the baseline is reading matched files in full, which is how coding agents often explore unfamiliar code.
396450

397-
Stats are stored in `~/.semble/savings.jsonl`.
451+
Stats are stored in the OS cache folder (`~/Library/Caches/semble/` on macOS, `~/.cache/semble/` on Linux, `%LOCALAPPDATA%\semble\Cache\` on Windows).
398452

399453
</details>
400454

benchmarks/baselines/ablations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
)
1818
from benchmarks.run_benchmark import RepoResult, evaluate
1919
from semble import SembleIndex
20-
from semble.index.dense import _DEFAULT_MODEL_NAME
20+
from semble.utils import DEFAULT_MODEL_NAME
2121

2222
# alpha=None → raw mode, input depends on query
2323
# alpha=0.0 → hybrid pipeline, BM25-only input
@@ -129,7 +129,7 @@ def main() -> None:
129129

130130
summary = {
131131
"tool": "semble-ablations",
132-
"model": _DEFAULT_MODEL_NAME,
132+
"model": DEFAULT_MODEL_NAME,
133133
"by_mode": summarize_modes(results, modes),
134134
"repos": [asdict(r) for r in results],
135135
}

benchmarks/run_benchmark.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
)
1717
from benchmarks.metrics import ndcg_at_k, target_rank
1818
from semble import SembleIndex
19-
from semble.index.dense import _DEFAULT_MODEL_NAME
2019
from semble.types import SearchResult
20+
from semble.utils import DEFAULT_MODEL_NAME
2121

2222
_LATENCY_RUNS = 5
2323
_DIRECT_TOP_K = 10
@@ -259,7 +259,7 @@ def _save_results(results: list[RepoResult]) -> None:
259259
n_repos = len(results)
260260
output = {
261261
"tool": "semble-hybrid",
262-
"model": _DEFAULT_MODEL_NAME,
262+
"model": DEFAULT_MODEL_NAME,
263263
"summary": {
264264
"ndcg10": round(sum(r.ndcg10 for r in results) / n_repos, 4),
265265
"tokens": round(sum(r.tokens for r in results) / n_repos, 0),

benchmarks/speed_benchmark.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from benchmarks.data import RepoSpec, Task, available_repo_specs, load_tasks, save_results
1212
from benchmarks.tools import run_colgrep_files, run_ripgrep_count
1313
from semble import SembleIndex
14-
from semble.index.dense import _DEFAULT_MODEL_NAME
1514
from semble.types import EmbeddingMatrix
15+
from semble.utils import DEFAULT_MODEL_NAME
1616

1717
# One representative repo per language (medium size, healthy NDCG on the main benchmark).
1818
_REPOS: list[str] = [
@@ -192,7 +192,7 @@ def main() -> None:
192192

193193
print("Loading semble model...", file=sys.stderr)
194194
started = time.perf_counter()
195-
semble_model = StaticModel.from_pretrained(_DEFAULT_MODEL_NAME)
195+
semble_model = StaticModel.from_pretrained(DEFAULT_MODEL_NAME)
196196
print(f" loaded in {(time.perf_counter() - started) * 1000:.0f}ms", file=sys.stderr)
197197

198198
print("Loading CodeRankEmbed...", file=sys.stderr)

benchmarks/token_efficiency.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
target_matches_location,
2525
)
2626
from semble import SembleIndex
27-
from semble.index.dense import _DEFAULT_MODEL_NAME
2827
from semble.index.file_walker import DEFAULT_IGNORED_DIRS, FILE_TYPES, FileCategory
2928
from semble.ranking.boosting import _STOPWORDS as _SEMBLE_STOPWORDS
3029
from semble.types import Chunk
30+
from semble.utils import DEFAULT_MODEL_NAME
3131

3232
_RG_INCLUDE_GLOBS: tuple[str, ...] = tuple(
3333
f"*{ext}" for ext, spec in FILE_TYPES.items() if spec.category == FileCategory.CODE
@@ -378,7 +378,7 @@ def run_recall(args: argparse.Namespace) -> None:
378378

379379
print("Loading tokenizer + model...", file=sys.stderr)
380380
enc = tiktoken.get_encoding(_TOKENIZER_NAME)
381-
model = StaticModel.from_pretrained(_DEFAULT_MODEL_NAME)
381+
model = StaticModel.from_pretrained(DEFAULT_MODEL_NAME)
382382

383383
method_curves: dict[str, MethodCurves] = defaultdict(list)
384384
print(f"\n{'Repo':<22} {'Language':<12} {'Tasks':>6} {'Time':>8}", file=sys.stderr)

src/semble/agents/claude.md

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,7 @@ semble search "save_pretrained" ./my-project
1212
semble search "save model to disk" ./my-project --top-k 10
1313
```
1414

15-
If you anticipate doing more than one search, use `semble index` to create an index.
16-
17-
```bash
18-
semble index ./my-project -o my_index
19-
```
20-
21-
You can then reuse this index later on:
22-
23-
```bash
24-
semble search "save_pretrained" --index my_index
25-
```
26-
27-
An index is not automatically updated, so if the code changes significantly, reindex. If you notice stale results while resolving searches to files, reindex.
15+
Results are cached automatically on first run and invalidated when files change.
2816

2917
Use `--content docs` to search documentation and prose, `--content config` for config files (yaml, toml, etc.), or `--content all` to search code, docs, and config:
3018

@@ -40,17 +28,14 @@ Use `semble find-related` to discover code similar to a known location (pass `fi
4028
semble find-related src/auth.py 42 ./my-project
4129
```
4230

43-
Like search, `find-related` also accepts an `--index` argument.
44-
4531
`path` defaults to the current directory when omitted; git URLs are accepted.
4632

4733
If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place.
4834

4935
### Workflow
5036

51-
1. Index the repo using `semble index -o cached_index`.
52-
2. Start with `semble search` to find relevant chunks. Pass the index to achieve results faster.
53-
3. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
54-
4. Inspect full files only when the returned chunk does not give enough context.
55-
5. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
56-
6. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.
37+
1. Start with `semble search` to find relevant chunks. The index is built and cached automatically.
38+
2. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
39+
3. Inspect full files only when the returned chunk does not give enough context.
40+
4. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
41+
5. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.

src/semble/agents/copilot.md

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,7 @@ semble search "save_pretrained" ./my-project
1212
semble search "save model to disk" ./my-project --top-k 10
1313
```
1414

15-
If you anticipate doing more than one search, use `semble index` to create an index.
16-
17-
```bash
18-
semble index ./my-project -o my_index
19-
```
20-
21-
You can then reuse this index later on:
22-
23-
```bash
24-
semble search "save_pretrained" --index my_index
25-
```
26-
27-
An index is not automatically updated, so if the code changes significantly, reindex. If you notice stale results while resolving searches to files, reindex.
15+
Results are cached automatically on first run and invalidated when files change.
2816

2917
Use `--content docs` to search documentation and prose, `--content config` for config files (yaml, toml, etc.), or `--content all` to search code, docs, and config:
3018

@@ -40,17 +28,14 @@ Use `semble find-related` to discover code similar to a known location (pass `fi
4028
semble find-related src/auth.py 42 ./my-project
4129
```
4230

43-
Like search, `find-related` also accepts an `--index` argument.
44-
4531
`path` defaults to the current directory when omitted; git URLs are accepted.
4632

4733
If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place.
4834

4935
### Workflow
5036

51-
1. Index the repo using `semble index -o cached_index`.
52-
2. Start with `semble search` to find relevant chunks. Pass the index to achieve results faster.
53-
3. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
54-
4. Inspect full files only when the returned chunk does not give enough context.
55-
5. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
56-
6. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.
37+
1. Start with `semble search` to find relevant chunks. The index is built and cached automatically.
38+
2. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
39+
3. Inspect full files only when the returned chunk does not give enough context.
40+
4. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
41+
5. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.

src/semble/agents/cursor.md

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,7 @@ semble search "save_pretrained" ./my-project
1111
semble search "save model to disk" ./my-project --top-k 10
1212
```
1313

14-
If you anticipate doing more than one search, use `semble index` to create an index.
15-
16-
```bash
17-
semble index ./my-project -o my_index
18-
```
19-
20-
You can then reuse this index later on:
21-
22-
```bash
23-
semble search "save_pretrained" --index my_index
24-
```
25-
26-
An index is not automatically updated, so if the code changes significantly, reindex. If you notice stale results while resolving searches to files, reindex.
14+
Results are cached automatically on first run and invalidated when files change.
2715

2816
Use `--content docs` to search documentation and prose, `--content config` for config files (yaml, toml, etc.), or `--content all` to search code, docs, and config:
2917

@@ -39,17 +27,14 @@ Use `semble find-related` to discover code similar to a known location (pass `fi
3927
semble find-related src/auth.py 42 ./my-project
4028
```
4129

42-
Like search, `find-related` also accepts an `--index` argument.
43-
4430
`path` defaults to the current directory when omitted; git URLs are accepted.
4531

4632
If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place.
4733

4834
### Workflow
4935

50-
1. Index the repo using `semble index -o cached_index`.
51-
2. Start with `semble search` to find relevant chunks. Pass the index to achieve results faster.
52-
3. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
53-
4. Inspect full files only when the returned chunk does not give enough context.
54-
5. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
55-
6. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.
36+
1. Start with `semble search` to find relevant chunks. The index is built and cached automatically.
37+
2. Use `--content docs` for documentation, `--content config` for config files, or `--content all` for everything.
38+
3. Inspect full files only when the returned chunk does not give enough context.
39+
4. Optionally use `semble find-related` with a promising result's `file_path` and `line` to discover related implementations.
40+
5. Use grep only when you need exhaustive literal matches or quick confirmation of an exact string.

0 commit comments

Comments
 (0)