odino 0.1.1__tar.gz → 0.1.4__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.
- {odino-0.1.1 → odino-0.1.4}/PKG-INFO +20 -1
- {odino-0.1.1 → odino-0.1.4}/README.md +19 -0
- {odino-0.1.1 → odino-0.1.4}/odino/__init__.py +2 -2
- {odino-0.1.1 → odino-0.1.4}/odino/cli.py +118 -72
- {odino-0.1.1 → odino-0.1.4}/odino/indexer.py +155 -114
- {odino-0.1.1 → odino-0.1.4}/odino/searcher.py +136 -103
- {odino-0.1.1 → odino-0.1.4}/odino/utils.py +78 -20
- {odino-0.1.1 → odino-0.1.4}/odino.egg-info/PKG-INFO +20 -1
- {odino-0.1.1 → odino-0.1.4}/pyproject.toml +1 -1
- {odino-0.1.1 → odino-0.1.4}/setup.py +2 -2
- {odino-0.1.1 → odino-0.1.4}/LICENSE +0 -0
- {odino-0.1.1 → odino-0.1.4}/odino.egg-info/SOURCES.txt +0 -0
- {odino-0.1.1 → odino-0.1.4}/odino.egg-info/dependency_links.txt +0 -0
- {odino-0.1.1 → odino-0.1.4}/odino.egg-info/entry_points.txt +0 -0
- {odino-0.1.1 → odino-0.1.4}/odino.egg-info/requires.txt +0 -0
- {odino-0.1.1 → odino-0.1.4}/odino.egg-info/top_level.txt +0 -0
- {odino-0.1.1 → odino-0.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odino
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Local semantic search CLI tool for codebases using embeddings
|
|
5
5
|
Home-page: https://github.com/cesp99/odino
|
|
6
6
|
Author: Carlo Esposito
|
|
@@ -36,6 +36,13 @@ Dynamic: requires-python
|
|
|
36
36
|
|
|
37
37
|
A fast local semantic search tool that helps you find code using natural language queries. No internet required, everything runs locally using the embeddinggemma-300m model.
|
|
38
38
|
|
|
39
|
+
<p align="center">
|
|
40
|
+
<a href="https://pypi.org/project/odino/"><img alt="PyPI" src="https://badge.fury.io/py/odino.svg"></a>
|
|
41
|
+
<a href="https://www.gnu.org/licenses/gpl-3.0"><img alt="License: GPL v3" src="https://img.shields.io/badge/License-GPLv3-blue.svg"></a>
|
|
42
|
+
<a href="https://pypi.org/project/odino"><img alt="Supported Python Versions" src="https://img.shields.io/pypi/pyversions/odino?color=brightgreen"></a>
|
|
43
|
+
<a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
|
|
44
|
+
</p>
|
|
45
|
+
|
|
39
46
|
## Quick Start
|
|
40
47
|
|
|
41
48
|
Install Odino directly from PyPI:
|
|
@@ -235,6 +242,18 @@ For advanced memory management configuration and more detailed troubleshooting,
|
|
|
235
242
|
4. Add tests if applicable
|
|
236
243
|
5. Submit a pull request
|
|
237
244
|
|
|
245
|
+
## For AI Agents
|
|
246
|
+
|
|
247
|
+
AI agents working with this codebase should refer to the [ODINO.md](ODINO.md) file for detailed usage instructions and best practices. This file contains comprehensive documentation on:
|
|
248
|
+
|
|
249
|
+
- **Basic Commands**: Indexing and searching operations
|
|
250
|
+
- **Advanced Search Options**: Filtering, path targeting, and result limiting
|
|
251
|
+
- **Semantic Search Capabilities**: How to find files by meaning rather than exact keywords
|
|
252
|
+
- **Best Practices**: When to use Odino vs traditional grep, filtering strategies, and query optimization
|
|
253
|
+
- **Workflow Examples**: Real-world usage patterns for code discovery
|
|
254
|
+
|
|
255
|
+
The ODINO.md file is specifically designed to help AI agents understand how to effectively use Odino's semantic search capabilities to navigate and understand codebases during development tasks.
|
|
256
|
+
|
|
238
257
|
## License
|
|
239
258
|
|
|
240
259
|
This project is licensed under the GNU General Public License v3.0 - see [LICENSE](LICENSE) file for details.
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
A fast local semantic search tool that helps you find code using natural language queries. No internet required, everything runs locally using the embeddinggemma-300m model.
|
|
4
4
|
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://pypi.org/project/odino/"><img alt="PyPI" src="https://badge.fury.io/py/odino.svg"></a>
|
|
7
|
+
<a href="https://www.gnu.org/licenses/gpl-3.0"><img alt="License: GPL v3" src="https://img.shields.io/badge/License-GPLv3-blue.svg"></a>
|
|
8
|
+
<a href="https://pypi.org/project/odino"><img alt="Supported Python Versions" src="https://img.shields.io/pypi/pyversions/odino?color=brightgreen"></a>
|
|
9
|
+
<a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
5
12
|
## Quick Start
|
|
6
13
|
|
|
7
14
|
Install Odino directly from PyPI:
|
|
@@ -201,6 +208,18 @@ For advanced memory management configuration and more detailed troubleshooting,
|
|
|
201
208
|
4. Add tests if applicable
|
|
202
209
|
5. Submit a pull request
|
|
203
210
|
|
|
211
|
+
## For AI Agents
|
|
212
|
+
|
|
213
|
+
AI agents working with this codebase should refer to the [ODINO.md](ODINO.md) file for detailed usage instructions and best practices. This file contains comprehensive documentation on:
|
|
214
|
+
|
|
215
|
+
- **Basic Commands**: Indexing and searching operations
|
|
216
|
+
- **Advanced Search Options**: Filtering, path targeting, and result limiting
|
|
217
|
+
- **Semantic Search Capabilities**: How to find files by meaning rather than exact keywords
|
|
218
|
+
- **Best Practices**: When to use Odino vs traditional grep, filtering strategies, and query optimization
|
|
219
|
+
- **Workflow Examples**: Real-world usage patterns for code discovery
|
|
220
|
+
|
|
221
|
+
The ODINO.md file is specifically designed to help AI agents understand how to effectively use Odino's semantic search capabilities to navigate and understand codebases during development tasks.
|
|
222
|
+
|
|
204
223
|
## License
|
|
205
224
|
|
|
206
225
|
This project is licensed under the GNU General Public License v3.0 - see [LICENSE](LICENSE) file for details.
|
|
@@ -23,23 +23,58 @@ app = typer.Typer(
|
|
|
23
23
|
)
|
|
24
24
|
console = Console()
|
|
25
25
|
|
|
26
|
+
|
|
26
27
|
def version_callback(value: bool) -> None:
|
|
27
28
|
"""Show version information."""
|
|
28
29
|
if value:
|
|
29
|
-
console.print("Odino
|
|
30
|
-
console.print("Using embeddinggemma-300m model for fast indexing")
|
|
30
|
+
console.print("Odino v0.1.4 - Local Semantic Search CLI")
|
|
31
31
|
raise typer.Exit()
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@app.callback(invoke_without_command=True)
|
|
35
35
|
def main(
|
|
36
36
|
ctx: typer.Context,
|
|
37
|
-
version: Annotated[
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
version: Annotated[
|
|
38
|
+
bool,
|
|
39
|
+
typer.Option(
|
|
40
|
+
"--version",
|
|
41
|
+
"-v",
|
|
42
|
+
help="Show version and exit",
|
|
43
|
+
callback=version_callback,
|
|
44
|
+
is_eager=True,
|
|
45
|
+
),
|
|
46
|
+
] = None,
|
|
47
|
+
query_text: Annotated[
|
|
48
|
+
Optional[str],
|
|
49
|
+
typer.Option("--query", "-q", help="Search query in natural language"),
|
|
50
|
+
] = None,
|
|
51
|
+
results: Annotated[
|
|
52
|
+
Optional[int],
|
|
53
|
+
typer.Option(
|
|
54
|
+
"--results", "-r", help="Number of results to return", min=1, max=100
|
|
55
|
+
),
|
|
56
|
+
] = None,
|
|
57
|
+
include: Annotated[
|
|
58
|
+
Optional[str],
|
|
59
|
+
typer.Option(
|
|
60
|
+
"--include", help="Include only files matching this pattern (glob)"
|
|
61
|
+
),
|
|
62
|
+
] = None,
|
|
63
|
+
exclude: Annotated[
|
|
64
|
+
Optional[str],
|
|
65
|
+
typer.Option("--exclude", help="Exclude files matching this pattern (glob)"),
|
|
66
|
+
] = None,
|
|
67
|
+
path: Annotated[
|
|
68
|
+
Optional[Path],
|
|
69
|
+
typer.Option(
|
|
70
|
+
"--path",
|
|
71
|
+
"-p",
|
|
72
|
+
help="Directory to search in",
|
|
73
|
+
exists=True,
|
|
74
|
+
file_okay=False,
|
|
75
|
+
dir_okay=True,
|
|
76
|
+
),
|
|
77
|
+
] = None,
|
|
43
78
|
) -> None:
|
|
44
79
|
"""Odino - Local semantic search CLI for codebases."""
|
|
45
80
|
if ctx.invoked_subcommand is None:
|
|
@@ -96,43 +131,50 @@ def index(
|
|
|
96
131
|
"""Index files in a directory for semantic search."""
|
|
97
132
|
try:
|
|
98
133
|
config = load_config(path)
|
|
99
|
-
|
|
134
|
+
|
|
100
135
|
# Update config with command line options
|
|
101
136
|
config["model_name"] = model
|
|
102
137
|
config["chunk_size"] = chunk_size
|
|
103
138
|
config["chunk_overlap"] = chunk_overlap
|
|
104
|
-
|
|
139
|
+
|
|
105
140
|
save_config(path, config)
|
|
106
|
-
|
|
107
|
-
console.print(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
141
|
+
|
|
142
|
+
console.print(
|
|
143
|
+
Panel.fit(
|
|
144
|
+
f"[bold blue]Indexing directory:[/bold blue] {path.absolute()}\n"
|
|
145
|
+
f"[bold blue]Model:[/bold blue] {model}\n"
|
|
146
|
+
f"[bold blue]Chunk size:[/bold blue] {chunk_size}\n"
|
|
147
|
+
f"[bold blue]Chunk overlap:[/bold blue] {chunk_overlap}",
|
|
148
|
+
title="Odino Indexer",
|
|
149
|
+
border_style="blue",
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
|
|
116
153
|
indexer = Indexer(path, console)
|
|
117
|
-
|
|
154
|
+
|
|
118
155
|
with Progress(
|
|
119
156
|
SpinnerColumn(),
|
|
120
157
|
TextColumn("[progress.description]{task.description}"),
|
|
121
158
|
console=console,
|
|
122
159
|
) as progress:
|
|
123
160
|
task = progress.add_task("Initializing...", total=None)
|
|
124
|
-
|
|
125
|
-
stats = indexer.index(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
161
|
+
|
|
162
|
+
stats = indexer.index(
|
|
163
|
+
force=force,
|
|
164
|
+
progress_callback=lambda msg: progress.update(task, description=msg),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
console.print(
|
|
168
|
+
Panel.fit(
|
|
169
|
+
f"[bold green]Indexing complete![/bold green]\n"
|
|
170
|
+
f"Files indexed: {stats['files_indexed']}\n"
|
|
171
|
+
f"Chunks created: {stats['chunks_created']}\n"
|
|
172
|
+
f"Total size: {stats['total_size_mb']:.1f} MB",
|
|
173
|
+
title="Success",
|
|
174
|
+
border_style="green",
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
|
|
136
178
|
except Exception as e:
|
|
137
179
|
console.print(f"[bold red]Error during indexing:[/bold red] {e}")
|
|
138
180
|
raise typer.Exit(1)
|
|
@@ -175,19 +217,23 @@ def query(
|
|
|
175
217
|
"""Search indexed files using natural language queries."""
|
|
176
218
|
try:
|
|
177
219
|
config = load_config(path)
|
|
178
|
-
|
|
179
|
-
effective_results =
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
220
|
+
|
|
221
|
+
effective_results = (
|
|
222
|
+
results if results is not None else config.get("max_results", 2)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
console.print(
|
|
226
|
+
Panel.fit(
|
|
227
|
+
f"[bold blue]Searching:[/bold blue] {query_text}\n"
|
|
228
|
+
f"[bold blue]Directory:[/bold blue] {path.absolute()}\n"
|
|
229
|
+
f"[bold blue]Max results:[/bold blue] {effective_results}",
|
|
230
|
+
title="Odino Search",
|
|
231
|
+
border_style="blue",
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
189
235
|
searcher = Searcher(path, console)
|
|
190
|
-
|
|
236
|
+
|
|
191
237
|
with console.status("[bold blue]Searching..."):
|
|
192
238
|
search_results = searcher.search(
|
|
193
239
|
query_text,
|
|
@@ -195,23 +241,23 @@ def query(
|
|
|
195
241
|
include_pattern=include,
|
|
196
242
|
exclude_pattern=exclude,
|
|
197
243
|
)
|
|
198
|
-
|
|
244
|
+
|
|
199
245
|
if not search_results:
|
|
200
246
|
console.print("[yellow]No results found. Try a different query.[/yellow]")
|
|
201
247
|
return
|
|
202
|
-
|
|
248
|
+
|
|
203
249
|
# Display results in a table
|
|
204
250
|
table = Table(show_header=True, header_style="bold magenta")
|
|
205
251
|
table.add_column("File", style="cyan", no_wrap=False)
|
|
206
252
|
table.add_column("Score", style="green", width=8)
|
|
207
253
|
table.add_column("Content", style="white")
|
|
208
|
-
|
|
254
|
+
|
|
209
255
|
for result in search_results:
|
|
210
256
|
file_path = result.file_path
|
|
211
257
|
score = f"{result.score:.3f}"
|
|
212
258
|
content = result.content
|
|
213
259
|
start_line = result.start_line
|
|
214
|
-
|
|
260
|
+
|
|
215
261
|
# Create syntax highlighted content
|
|
216
262
|
try:
|
|
217
263
|
syntax = Syntax(
|
|
@@ -226,10 +272,10 @@ def query(
|
|
|
226
272
|
except Exception:
|
|
227
273
|
# Fallback to plain text
|
|
228
274
|
table.add_row(file_path, score, content)
|
|
229
|
-
|
|
275
|
+
|
|
230
276
|
console.print(table)
|
|
231
277
|
console.print(f"\n[bold green]Found {len(search_results)} results[/bold green]")
|
|
232
|
-
|
|
278
|
+
|
|
233
279
|
except Exception as e:
|
|
234
280
|
console.print(f"[bold red]Error during search:[/bold red] {e}")
|
|
235
281
|
raise typer.Exit(1)
|
|
@@ -249,37 +295,37 @@ def status(
|
|
|
249
295
|
try:
|
|
250
296
|
config = load_config(path)
|
|
251
297
|
odino_dir = path / ".odino"
|
|
252
|
-
|
|
298
|
+
|
|
253
299
|
if not odino_dir.exists():
|
|
254
300
|
console.print("[yellow]No index found. Run 'odino index' first.[/yellow]")
|
|
255
301
|
return
|
|
256
|
-
|
|
302
|
+
|
|
257
303
|
# Get index info
|
|
258
304
|
from .searcher import Searcher
|
|
305
|
+
|
|
259
306
|
searcher = Searcher(path, config)
|
|
260
307
|
index_info = searcher.get_index_info()
|
|
261
|
-
|
|
262
|
-
console.print(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
308
|
+
|
|
309
|
+
console.print(
|
|
310
|
+
Panel.fit(
|
|
311
|
+
f"[bold blue]Configuration:[/bold blue]\n"
|
|
312
|
+
f" Model: {config['model_name']}\n"
|
|
313
|
+
f" Chunk size: {config['chunk_size']}\n"
|
|
314
|
+
f" Chunk overlap: {config['chunk_overlap']}\n"
|
|
315
|
+
f" Max results: {config['max_results']}\n\n"
|
|
316
|
+
f"[bold blue]Index Status:[/bold blue]\n"
|
|
317
|
+
f" Total chunks: {index_info['total_chunks']}\n"
|
|
318
|
+
f" Indexed files: {index_info['indexed_files']}\n"
|
|
319
|
+
f" Last updated: {index_info['last_updated']}",
|
|
320
|
+
title="Odino Status",
|
|
321
|
+
border_style="blue",
|
|
322
|
+
)
|
|
323
|
+
)
|
|
324
|
+
|
|
276
325
|
except Exception as e:
|
|
277
326
|
console.print(f"[bold red]Error getting status:[/bold red] {e}")
|
|
278
327
|
raise typer.Exit(1)
|
|
279
328
|
|
|
280
329
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
330
|
if __name__ == "__main__":
|
|
285
|
-
app()
|
|
331
|
+
app()
|