embgrep 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .env
10
+ .env.local
11
+ CLAUDE.md
12
+ PLAN.txt
embgrep-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QuartzUnit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
embgrep-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.4
2
+ Name: embgrep
3
+ Version: 0.1.0
4
+ Summary: Local semantic search — embedding-powered grep for files, zero external services.
5
+ Project-URL: Homepage, https://github.com/QuartzUnit/embgrep
6
+ Project-URL: Repository, https://github.com/QuartzUnit/embgrep
7
+ Author: QuartzUnit
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: embeddings,grep,local,mcp,rag,semantic-search
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: fastembed>=0.4
20
+ Requires-Dist: numpy>=1.24
21
+ Provides-Extra: all
22
+ Requires-Dist: click>=8.0; extra == 'all'
23
+ Requires-Dist: fastmcp>=2.0; extra == 'all'
24
+ Requires-Dist: rich>=13.0; extra == 'all'
25
+ Provides-Extra: cli
26
+ Requires-Dist: click>=8.0; extra == 'cli'
27
+ Requires-Dist: rich>=13.0; extra == 'cli'
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.8; extra == 'dev'
31
+ Provides-Extra: mcp
32
+ Requires-Dist: fastmcp>=2.0; extra == 'mcp'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # embgrep
36
+
37
+ **Local semantic search — embedding-powered grep for files, zero external services.**
38
+
39
+ [![PyPI](https://img.shields.io/pypi/v/embgrep)](https://pypi.org/project/embgrep/)
40
+ [![Python](https://img.shields.io/pypi/pyversions/embgrep)](https://pypi.org/project/embgrep/)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
42
+
43
+ Search your codebase and documentation by *meaning*, not just keywords. embgrep indexes files into local embeddings and lets you run semantic queries — no API keys, no cloud services, no vector database servers.
44
+
45
+ ## Features
46
+
47
+ - **Local embeddings** — Uses [fastembed](https://github.com/qdrant/fastembed) (ONNX Runtime), no API keys needed
48
+ - **SQLite storage** — Single-file index, no external vector DB
49
+ - **Incremental indexing** — Only re-indexes changed files (SHA-256 hash comparison)
50
+ - **Smart chunking** — Function-level splitting for code, heading-level for docs
51
+ - **MCP native** — 4-tool FastMCP server for LLM agent integration
52
+ - **15+ file types** — `.py`, `.js`, `.ts`, `.java`, `.go`, `.rs`, `.md`, `.txt`, `.yaml`, `.json`, `.toml`, and more
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install embgrep # core (fastembed + numpy)
58
+ pip install embgrep[cli] # + click/rich CLI
59
+ pip install embgrep[mcp] # + FastMCP server
60
+ pip install embgrep[all] # everything
61
+ ```
62
+
63
+ ## Quick Start
64
+
65
+ ### Python API
66
+
67
+ ```python
68
+ from embgrep import EmbGrep
69
+
70
+ eg = EmbGrep()
71
+
72
+ # Index a directory
73
+ eg.index("./my-project", patterns=["*.py", "*.md"])
74
+
75
+ # Semantic search
76
+ results = eg.search("database connection pooling", top_k=5)
77
+ for r in results:
78
+ print(f"{r.file_path}:{r.line_start}-{r.line_end} (score: {r.score:.4f})")
79
+ print(f" {r.chunk_text[:80]}...")
80
+
81
+ # Incremental update (only changed files)
82
+ eg.update()
83
+
84
+ # Index statistics
85
+ status = eg.status()
86
+ print(f"{status.total_files} files, {status.total_chunks} chunks, {status.index_size_mb} MB")
87
+
88
+ eg.close()
89
+ ```
90
+
91
+ ### CLI
92
+
93
+ ```bash
94
+ # Index a project
95
+ embgrep index ./my-project --patterns "*.py,*.md"
96
+
97
+ # Search
98
+ embgrep search "error handling patterns"
99
+
100
+ # Filter by file type
101
+ embgrep search "async database query" --path-filter "%.py"
102
+
103
+ # Check status
104
+ embgrep status
105
+
106
+ # Update changed files
107
+ embgrep update
108
+ ```
109
+
110
+ ### Convenience functions
111
+
112
+ ```python
113
+ import embgrep
114
+
115
+ embgrep.index("./src")
116
+ results = embgrep.search("authentication middleware")
117
+ status = embgrep.status()
118
+ embgrep.update()
119
+ ```
120
+
121
+ ## MCP Server
122
+
123
+ Add to your Claude Desktop / MCP client configuration:
124
+
125
+ ```json
126
+ {
127
+ "mcpServers": {
128
+ "embgrep": {
129
+ "command": "embgrep-mcp"
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ Or with uvx:
136
+
137
+ ```json
138
+ {
139
+ "mcpServers": {
140
+ "embgrep": {
141
+ "command": "uvx",
142
+ "args": ["--from", "embgrep[mcp]", "embgrep-mcp"]
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### MCP Tools
149
+
150
+ | Tool | Description |
151
+ |------|-------------|
152
+ | `index_directory` | Index files in a directory for semantic search |
153
+ | `semantic_search` | Search indexed files using natural language |
154
+ | `index_status` | Get current index statistics |
155
+ | `update_index` | Incremental update — re-index changed files only |
156
+
157
+ ## How It Works
158
+
159
+ 1. **Chunking** — Files are split into semantically meaningful chunks:
160
+ - Code files (`.py`, `.js`, `.ts`, etc.): split by function/class boundaries
161
+ - Documents (`.md`, `.txt`): split by headings or paragraph breaks
162
+ - Config files: fixed-size chunking
163
+
164
+ 2. **Embedding** — Each chunk is converted to a 384-dimensional vector using [BGE-small-en-v1.5](https://huggingface.co/BAAI/bge-small-en-v1.5) via ONNX Runtime (no PyTorch needed)
165
+
166
+ 3. **Storage** — Embeddings are stored as BLOBs in a local SQLite database
167
+
168
+ 4. **Search** — Query text is embedded and compared against all chunks using cosine similarity
169
+
170
+ ## Configuration
171
+
172
+ | Parameter | Default | Description |
173
+ |-----------|---------|-------------|
174
+ | `db_path` | `~/.local/share/embgrep/embgrep.db` | SQLite database location |
175
+ | `model` | `BAAI/bge-small-en-v1.5` | fastembed model name |
176
+ | `max_chunk_size` | 1000 chars | Maximum chunk size for fixed-size splitting |
177
+ | `top_k` | 5 | Number of search results |
178
+
179
+ ## QuartzUnit Ecosystem
180
+
181
+ | Package | Description |
182
+ |---------|-------------|
183
+ | [markgrab](https://github.com/QuartzUnit/markgrab) | HTML/YouTube/PDF/DOCX to LLM-ready markdown |
184
+ | [snapgrab](https://github.com/QuartzUnit/snapgrab) | URL to screenshot + metadata |
185
+ | [docpick](https://github.com/QuartzUnit/docpick) | OCR + LLM document structure extraction |
186
+ | [browsegrab](https://github.com/QuartzUnit/browsegrab) | Local LLM browser agent |
187
+ | [feedkit](https://github.com/QuartzUnit/feedkit) | RSS feed collection + MCP |
188
+ | **embgrep** | **Local semantic search for files** |
189
+
190
+ ## License
191
+
192
+ MIT
193
+
194
+ <!-- mcp-name: io.github.ArkNill/embgrep -->
@@ -0,0 +1,160 @@
1
+ # embgrep
2
+
3
+ **Local semantic search — embedding-powered grep for files, zero external services.**
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/embgrep)](https://pypi.org/project/embgrep/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/embgrep)](https://pypi.org/project/embgrep/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ Search your codebase and documentation by *meaning*, not just keywords. embgrep indexes files into local embeddings and lets you run semantic queries — no API keys, no cloud services, no vector database servers.
10
+
11
+ ## Features
12
+
13
+ - **Local embeddings** — Uses [fastembed](https://github.com/qdrant/fastembed) (ONNX Runtime), no API keys needed
14
+ - **SQLite storage** — Single-file index, no external vector DB
15
+ - **Incremental indexing** — Only re-indexes changed files (SHA-256 hash comparison)
16
+ - **Smart chunking** — Function-level splitting for code, heading-level for docs
17
+ - **MCP native** — 4-tool FastMCP server for LLM agent integration
18
+ - **15+ file types** — `.py`, `.js`, `.ts`, `.java`, `.go`, `.rs`, `.md`, `.txt`, `.yaml`, `.json`, `.toml`, and more
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pip install embgrep # core (fastembed + numpy)
24
+ pip install embgrep[cli] # + click/rich CLI
25
+ pip install embgrep[mcp] # + FastMCP server
26
+ pip install embgrep[all] # everything
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### Python API
32
+
33
+ ```python
34
+ from embgrep import EmbGrep
35
+
36
+ eg = EmbGrep()
37
+
38
+ # Index a directory
39
+ eg.index("./my-project", patterns=["*.py", "*.md"])
40
+
41
+ # Semantic search
42
+ results = eg.search("database connection pooling", top_k=5)
43
+ for r in results:
44
+ print(f"{r.file_path}:{r.line_start}-{r.line_end} (score: {r.score:.4f})")
45
+ print(f" {r.chunk_text[:80]}...")
46
+
47
+ # Incremental update (only changed files)
48
+ eg.update()
49
+
50
+ # Index statistics
51
+ status = eg.status()
52
+ print(f"{status.total_files} files, {status.total_chunks} chunks, {status.index_size_mb} MB")
53
+
54
+ eg.close()
55
+ ```
56
+
57
+ ### CLI
58
+
59
+ ```bash
60
+ # Index a project
61
+ embgrep index ./my-project --patterns "*.py,*.md"
62
+
63
+ # Search
64
+ embgrep search "error handling patterns"
65
+
66
+ # Filter by file type
67
+ embgrep search "async database query" --path-filter "%.py"
68
+
69
+ # Check status
70
+ embgrep status
71
+
72
+ # Update changed files
73
+ embgrep update
74
+ ```
75
+
76
+ ### Convenience functions
77
+
78
+ ```python
79
+ import embgrep
80
+
81
+ embgrep.index("./src")
82
+ results = embgrep.search("authentication middleware")
83
+ status = embgrep.status()
84
+ embgrep.update()
85
+ ```
86
+
87
+ ## MCP Server
88
+
89
+ Add to your Claude Desktop / MCP client configuration:
90
+
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "embgrep": {
95
+ "command": "embgrep-mcp"
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ Or with uvx:
102
+
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "embgrep": {
107
+ "command": "uvx",
108
+ "args": ["--from", "embgrep[mcp]", "embgrep-mcp"]
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### MCP Tools
115
+
116
+ | Tool | Description |
117
+ |------|-------------|
118
+ | `index_directory` | Index files in a directory for semantic search |
119
+ | `semantic_search` | Search indexed files using natural language |
120
+ | `index_status` | Get current index statistics |
121
+ | `update_index` | Incremental update — re-index changed files only |
122
+
123
+ ## How It Works
124
+
125
+ 1. **Chunking** — Files are split into semantically meaningful chunks:
126
+ - Code files (`.py`, `.js`, `.ts`, etc.): split by function/class boundaries
127
+ - Documents (`.md`, `.txt`): split by headings or paragraph breaks
128
+ - Config files: fixed-size chunking
129
+
130
+ 2. **Embedding** — Each chunk is converted to a 384-dimensional vector using [BGE-small-en-v1.5](https://huggingface.co/BAAI/bge-small-en-v1.5) via ONNX Runtime (no PyTorch needed)
131
+
132
+ 3. **Storage** — Embeddings are stored as BLOBs in a local SQLite database
133
+
134
+ 4. **Search** — Query text is embedded and compared against all chunks using cosine similarity
135
+
136
+ ## Configuration
137
+
138
+ | Parameter | Default | Description |
139
+ |-----------|---------|-------------|
140
+ | `db_path` | `~/.local/share/embgrep/embgrep.db` | SQLite database location |
141
+ | `model` | `BAAI/bge-small-en-v1.5` | fastembed model name |
142
+ | `max_chunk_size` | 1000 chars | Maximum chunk size for fixed-size splitting |
143
+ | `top_k` | 5 | Number of search results |
144
+
145
+ ## QuartzUnit Ecosystem
146
+
147
+ | Package | Description |
148
+ |---------|-------------|
149
+ | [markgrab](https://github.com/QuartzUnit/markgrab) | HTML/YouTube/PDF/DOCX to LLM-ready markdown |
150
+ | [snapgrab](https://github.com/QuartzUnit/snapgrab) | URL to screenshot + metadata |
151
+ | [docpick](https://github.com/QuartzUnit/docpick) | OCR + LLM document structure extraction |
152
+ | [browsegrab](https://github.com/QuartzUnit/browsegrab) | Local LLM browser agent |
153
+ | [feedkit](https://github.com/QuartzUnit/feedkit) | RSS feed collection + MCP |
154
+ | **embgrep** | **Local semantic search for files** |
155
+
156
+ ## License
157
+
158
+ MIT
159
+
160
+ <!-- mcp-name: io.github.ArkNill/embgrep -->
@@ -0,0 +1,79 @@
1
+ """embgrep — Local semantic search, embedding-powered grep for files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from embgrep.indexer import EmbGrep, IndexStatus, SearchResult
6
+
7
+ __all__ = ["EmbGrep", "IndexStatus", "SearchResult", "index", "search", "status", "update"]
8
+ __version__ = "0.1.0"
9
+
10
+
11
+ def index(directory: str, patterns: list[str] | None = None, db_path: str | None = None) -> dict:
12
+ """Index files in a directory.
13
+
14
+ Args:
15
+ directory: Path to the directory to index.
16
+ patterns: Optional list of glob patterns to filter files.
17
+ db_path: Optional path to the SQLite database.
18
+
19
+ Returns:
20
+ Dictionary with files_indexed, chunks_created, index_size_mb.
21
+ """
22
+ eg = EmbGrep(db_path=db_path) if db_path else EmbGrep()
23
+ try:
24
+ return eg.index(directory, patterns=patterns)
25
+ finally:
26
+ eg.close()
27
+
28
+
29
+ def search(
30
+ query: str, top_k: int = 5, path_filter: str | None = None, db_path: str | None = None
31
+ ) -> list[SearchResult]:
32
+ """Semantic search across indexed files.
33
+
34
+ Args:
35
+ query: Natural language search query.
36
+ top_k: Number of results to return.
37
+ path_filter: Optional LIKE pattern to filter by file path.
38
+ db_path: Optional path to the SQLite database.
39
+
40
+ Returns:
41
+ List of SearchResult sorted by similarity score.
42
+ """
43
+ eg = EmbGrep(db_path=db_path) if db_path else EmbGrep()
44
+ try:
45
+ return eg.search(query, top_k=top_k, path_filter=path_filter)
46
+ finally:
47
+ eg.close()
48
+
49
+
50
+ def status(db_path: str | None = None) -> IndexStatus:
51
+ """Get index statistics.
52
+
53
+ Args:
54
+ db_path: Optional path to the SQLite database.
55
+
56
+ Returns:
57
+ IndexStatus with total_files, total_chunks, last_updated, index_size_mb.
58
+ """
59
+ eg = EmbGrep(db_path=db_path) if db_path else EmbGrep()
60
+ try:
61
+ return eg.status()
62
+ finally:
63
+ eg.close()
64
+
65
+
66
+ def update(db_path: str | None = None) -> dict:
67
+ """Incremental update — re-index changed files only.
68
+
69
+ Args:
70
+ db_path: Optional path to the SQLite database.
71
+
72
+ Returns:
73
+ Dictionary with updated_files, new_chunks, removed_files.
74
+ """
75
+ eg = EmbGrep(db_path=db_path) if db_path else EmbGrep()
76
+ try:
77
+ return eg.update()
78
+ finally:
79
+ eg.close()
@@ -0,0 +1,141 @@
1
+ """CLI entry point for embgrep — embedding-powered grep for files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+
8
+ def main() -> None:
9
+ """Main CLI entry point."""
10
+ try:
11
+ import click
12
+ from rich.console import Console
13
+ from rich.table import Table
14
+ except ImportError:
15
+ print("CLI requires extra dependencies: pip install embgrep[cli]")
16
+ sys.exit(1)
17
+
18
+ console = Console()
19
+
20
+ @click.group()
21
+ @click.version_option(package_name="embgrep")
22
+ def cli() -> None:
23
+ """embgrep — Local semantic search, embedding-powered grep for files."""
24
+
25
+ @cli.command()
26
+ @click.argument("path", type=click.Path(exists=True))
27
+ @click.option("--patterns", "-p", default=None, help="Comma-separated glob patterns (e.g., '*.md,*.py').")
28
+ @click.option("--db-path", default=None, help="Path to SQLite database.")
29
+ @click.option("--model", default="BAAI/bge-small-en-v1.5", help="Embedding model name.")
30
+ def index(path: str, patterns: str | None, db_path: str | None, model: str) -> None:
31
+ """Index files in PATH for semantic search."""
32
+ from embgrep.indexer import EmbGrep
33
+
34
+ pattern_list = [p.strip() for p in patterns.split(",")] if patterns else None
35
+
36
+ kwargs: dict = {"model": model}
37
+ if db_path:
38
+ kwargs["db_path"] = db_path
39
+
40
+ eg = EmbGrep(**kwargs)
41
+ try:
42
+ with console.status("[bold green]Indexing files..."):
43
+ result = eg.index(path, patterns=pattern_list)
44
+ console.print(f"[green]Indexed {result['files_indexed']} files, {result['chunks_created']} chunks[/green]")
45
+ console.print(f"Index size: {result['index_size_mb']} MB")
46
+ finally:
47
+ eg.close()
48
+
49
+ @cli.command()
50
+ @click.argument("query")
51
+ @click.option("--top-k", "-k", default=5, help="Number of results to return.")
52
+ @click.option("--path-filter", "-f", default=None, help="SQL LIKE pattern for file path filter.")
53
+ @click.option("--db-path", default=None, help="Path to SQLite database.")
54
+ @click.option("--model", default="BAAI/bge-small-en-v1.5", help="Embedding model name.")
55
+ def search(query: str, top_k: int, path_filter: str | None, db_path: str | None, model: str) -> None:
56
+ """Semantic search across indexed files."""
57
+ from embgrep.indexer import EmbGrep
58
+
59
+ kwargs: dict = {"model": model}
60
+ if db_path:
61
+ kwargs["db_path"] = db_path
62
+
63
+ eg = EmbGrep(**kwargs)
64
+ try:
65
+ with console.status("[bold green]Searching..."):
66
+ results = eg.search(query, top_k=top_k, path_filter=path_filter)
67
+
68
+ if not results:
69
+ console.print("[yellow]No results found.[/yellow]")
70
+ return
71
+
72
+ table = Table(title=f"Search: {query!r}", show_lines=True)
73
+ table.add_column("#", style="dim", width=3)
74
+ table.add_column("Score", style="cyan", width=7)
75
+ table.add_column("File", style="green")
76
+ table.add_column("Lines", style="yellow", width=10)
77
+ table.add_column("Preview", max_width=60)
78
+
79
+ for i, r in enumerate(results, 1):
80
+ preview = r.chunk_text[:120].replace("\n", " ").strip()
81
+ if len(r.chunk_text) > 120:
82
+ preview += "..."
83
+ table.add_row(
84
+ str(i),
85
+ f"{r.score:.4f}",
86
+ r.file_path,
87
+ f"{r.line_start}-{r.line_end}",
88
+ preview,
89
+ )
90
+
91
+ console.print(table)
92
+ finally:
93
+ eg.close()
94
+
95
+ @cli.command()
96
+ @click.option("--db-path", default=None, help="Path to SQLite database.")
97
+ def status(db_path: str | None) -> None:
98
+ """Show index statistics."""
99
+ from embgrep.indexer import EmbGrep
100
+
101
+ kwargs: dict = {}
102
+ if db_path:
103
+ kwargs["db_path"] = db_path
104
+
105
+ eg = EmbGrep(**kwargs)
106
+ try:
107
+ st = eg.status()
108
+ console.print("[bold]embgrep Index Status[/bold]")
109
+ console.print(f" Files: {st.total_files}")
110
+ console.print(f" Chunks: {st.total_chunks}")
111
+ console.print(f" Last updated: {st.last_updated}")
112
+ console.print(f" Index size: {st.index_size_mb} MB")
113
+ finally:
114
+ eg.close()
115
+
116
+ @cli.command()
117
+ @click.option("--db-path", default=None, help="Path to SQLite database.")
118
+ @click.option("--model", default="BAAI/bge-small-en-v1.5", help="Embedding model name.")
119
+ def update(db_path: str | None, model: str) -> None:
120
+ """Incremental update — re-index changed files only."""
121
+ from embgrep.indexer import EmbGrep
122
+
123
+ kwargs: dict = {"model": model}
124
+ if db_path:
125
+ kwargs["db_path"] = db_path
126
+
127
+ eg = EmbGrep(**kwargs)
128
+ try:
129
+ with console.status("[bold green]Updating index..."):
130
+ result = eg.update()
131
+ console.print(f"[green]Updated {result['updated_files']} files, {result['new_chunks']} new chunks[/green]")
132
+ if result["removed_files"]:
133
+ console.print(f"[yellow]Removed {result['removed_files']} deleted files[/yellow]")
134
+ finally:
135
+ eg.close()
136
+
137
+ cli()
138
+
139
+
140
+ if __name__ == "__main__":
141
+ main()