indexter 0.1.2__py3-none-any.whl
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.
- indexter/__init__.py +9 -0
- indexter/cli/__init__.py +5 -0
- indexter/cli/cli.py +450 -0
- indexter/cli/config.py +77 -0
- indexter/cli/store.py +419 -0
- indexter/config.py +860 -0
- indexter/exceptions.py +10 -0
- indexter/mcp/__init__.py +0 -0
- indexter/mcp/prompts.py +62 -0
- indexter/mcp/server.py +220 -0
- indexter/mcp/tools.py +146 -0
- indexter/models.py +610 -0
- indexter/parser/__init__.py +3 -0
- indexter/parser/models.py +76 -0
- indexter/parser/parser.py +196 -0
- indexter/parser/parsers/__init__.py +0 -0
- indexter/parser/parsers/base.py +154 -0
- indexter/parser/parsers/chunk.py +59 -0
- indexter/parser/parsers/css.py +170 -0
- indexter/parser/parsers/html.py +344 -0
- indexter/parser/parsers/javascript.py +301 -0
- indexter/parser/parsers/json.py +154 -0
- indexter/parser/parsers/markdown.py +133 -0
- indexter/parser/parsers/python.py +220 -0
- indexter/parser/parsers/rust.py +332 -0
- indexter/parser/parsers/toml.py +173 -0
- indexter/parser/parsers/typescript.py +388 -0
- indexter/parser/parsers/yaml.py +186 -0
- indexter/store/__init__.py +3 -0
- indexter/store/models.py +74 -0
- indexter/store/store.py +626 -0
- indexter/walker/__init__.py +3 -0
- indexter/walker/models.py +32 -0
- indexter/walker/walker.py +396 -0
- indexter-0.1.2.dist-info/METADATA +609 -0
- indexter-0.1.2.dist-info/RECORD +38 -0
- indexter-0.1.2.dist-info/WHEEL +4 -0
- indexter-0.1.2.dist-info/entry_points.txt +4 -0
indexter/__init__.py
ADDED
indexter/cli/__init__.py
ADDED
indexter/cli/cli.py
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main CLI application and command definitions.
|
|
3
|
+
|
|
4
|
+
This module provides the main entry point for the Indexter CLI application,
|
|
5
|
+
which enables semantic code context retrieval for AI agents via RAG (Retrieval
|
|
6
|
+
Augmented Generation). It includes commands for initializing repositories,
|
|
7
|
+
indexing code, searching indexed content, and managing repository status.
|
|
8
|
+
|
|
9
|
+
The CLI is built using Typer for command-line parsing and Rich for enhanced
|
|
10
|
+
terminal output with colors, tables, and progress indicators.
|
|
11
|
+
|
|
12
|
+
Typical usage:
|
|
13
|
+
$ indexter init --path ~/code/repo-name
|
|
14
|
+
$ indexter index repo-name
|
|
15
|
+
$ indexter search "query" repo-name
|
|
16
|
+
$ indexter status
|
|
17
|
+
$ indexter forget repo-name
|
|
18
|
+
$ indexter config show
|
|
19
|
+
$ indexter config path
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# NOTE: This file uses 'cast' from typing to help with type checking of anyio.run()
|
|
23
|
+
# anyio.run() is typed as returning T | None, but in practice, the functions it calls
|
|
24
|
+
# always return a value of type T or raise an exception. To satisfy the type checker, we use
|
|
25
|
+
# cast() to assert the expected return type. This does not affect runtime behavior, only
|
|
26
|
+
# static type checking.
|
|
27
|
+
#
|
|
28
|
+
# e.g.:
|
|
29
|
+
# repo = cast(Repo, anyio.run(Repo.init, repo_path.resolve()))
|
|
30
|
+
#
|
|
31
|
+
# This tells the type checker that we expect Repo.init to return a Repo instance.
|
|
32
|
+
|
|
33
|
+
import logging
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Annotated, cast
|
|
36
|
+
|
|
37
|
+
import anyio
|
|
38
|
+
import typer
|
|
39
|
+
from rich.console import Console
|
|
40
|
+
from rich.logging import RichHandler
|
|
41
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
42
|
+
from rich.table import Table
|
|
43
|
+
|
|
44
|
+
from indexter import __version__
|
|
45
|
+
from indexter.exceptions import RepoExistsError, RepoNotFoundError
|
|
46
|
+
from indexter.models import Repo
|
|
47
|
+
from indexter.store import VectorStore
|
|
48
|
+
from indexter.store.models import IndexResult, SearchResults
|
|
49
|
+
|
|
50
|
+
from .config import config_app
|
|
51
|
+
from .store import store_app
|
|
52
|
+
|
|
53
|
+
app = typer.Typer(
|
|
54
|
+
name="indexter",
|
|
55
|
+
help="indexter - Enhanced codebase context for AI agents via RAG.",
|
|
56
|
+
no_args_is_help=True,
|
|
57
|
+
rich_markup_mode="rich",
|
|
58
|
+
)
|
|
59
|
+
app.add_typer(config_app, name="config")
|
|
60
|
+
app.add_typer(store_app, name="store")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
console = Console()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def version_callback(value: bool) -> None:
|
|
67
|
+
"""Print the application version and exit.
|
|
68
|
+
|
|
69
|
+
This callback is triggered when the --version flag is used. It displays
|
|
70
|
+
the current version of Indexter and exits the application.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
value: If True, print the version and exit. If False, do nothing.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
typer.Exit: Always raised when value is True to exit the application.
|
|
77
|
+
"""
|
|
78
|
+
if value:
|
|
79
|
+
console.print(f"indexter {__version__}")
|
|
80
|
+
raise typer.Exit()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@app.callback()
|
|
84
|
+
def main(
|
|
85
|
+
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable verbose output")] = False,
|
|
86
|
+
version: Annotated[
|
|
87
|
+
bool | None,
|
|
88
|
+
typer.Option("--version", callback=version_callback, is_eager=True, help="Show version"),
|
|
89
|
+
] = None,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""
|
|
92
|
+
Indexter - Semantic Code Context For Your LLM.
|
|
93
|
+
|
|
94
|
+
This is the main callback function for the CLI application. It sets up
|
|
95
|
+
logging configuration and handles global options like verbose output
|
|
96
|
+
and version display.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
verbose: Enable verbose debug logging output. Defaults to False.
|
|
100
|
+
version: When provided, displays version and exits. This parameter
|
|
101
|
+
is handled by version_callback. Defaults to None.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
None: This function configures logging and does not return a value.
|
|
105
|
+
"""
|
|
106
|
+
# Set up logging with rich handler
|
|
107
|
+
level = logging.DEBUG if verbose else logging.INFO
|
|
108
|
+
logging.basicConfig(
|
|
109
|
+
level=level,
|
|
110
|
+
format="%(message)s",
|
|
111
|
+
handlers=[RichHandler(console=console, show_time=False, show_path=False)],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@app.command()
|
|
116
|
+
def init(
|
|
117
|
+
path: Annotated[str, typer.Option("--path", "-p", help="Path to the git repository to index")] = ".",
|
|
118
|
+
no_index: Annotated[
|
|
119
|
+
bool,
|
|
120
|
+
typer.Option("--no-index", "-n", help="Do not index the repository after initialization", show_default=True),
|
|
121
|
+
] = False,
|
|
122
|
+
) -> None:
|
|
123
|
+
"""
|
|
124
|
+
Initialize a git repository for indexing.
|
|
125
|
+
|
|
126
|
+
Registers a git repository with Indexter, preparing it for semantic indexing.
|
|
127
|
+
This command validates the repository path, creates necessary metadata, and
|
|
128
|
+
adds it to the list of managed repositories.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
path: a string representing a filesystem path to the git repository to initialize.
|
|
132
|
+
The path will be resolved to an absolute path.
|
|
133
|
+
no_index: If True, do not index the repository after initialization.
|
|
134
|
+
Defaults to False.
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
typer.Exit: Exits with code 1 if the repository already exists or if an
|
|
138
|
+
unexpected error occurs during initialization or indexing.
|
|
139
|
+
|
|
140
|
+
Examples:
|
|
141
|
+
$ indexter init /home/user/projects/myrepo
|
|
142
|
+
✓ Added myrepo to indexter
|
|
143
|
+
|
|
144
|
+
Repository 'myrepo' initialized successfully and indexed successfully!
|
|
145
|
+
|
|
146
|
+
Next steps:
|
|
147
|
+
1. Use indexter search 'your query' myrepo to search the indexed code.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
async def _init() -> tuple[Repo, IndexResult | None]:
|
|
151
|
+
"""Run all init operations in a single event loop."""
|
|
152
|
+
async with VectorStore() as store:
|
|
153
|
+
resolved_path = Path(path).resolve()
|
|
154
|
+
repo = await Repo.init(resolved_path)
|
|
155
|
+
if no_index:
|
|
156
|
+
return repo, None
|
|
157
|
+
# Call index after init
|
|
158
|
+
result = await repo.index(store)
|
|
159
|
+
return repo, result
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
with Progress(
|
|
163
|
+
SpinnerColumn(),
|
|
164
|
+
TextColumn("[progress.description]{task.description}"),
|
|
165
|
+
console=console,
|
|
166
|
+
) as progress:
|
|
167
|
+
task_description = "Initializing..." if no_index else "Initializing and indexing..."
|
|
168
|
+
progress.add_task(task_description, total=None)
|
|
169
|
+
repo, result = cast(tuple[Repo, IndexResult | None], anyio.run(_init))
|
|
170
|
+
console.print(f"[green]✓[/green] Added [bold]{repo.name}[/bold] to Indexter")
|
|
171
|
+
except RepoExistsError as e:
|
|
172
|
+
console.print(f"[red]✗[/red] {e}")
|
|
173
|
+
raise typer.Exit(1) from e
|
|
174
|
+
except Exception as e:
|
|
175
|
+
console.print(f"[red]✗[/red] Unexpected error: {e}")
|
|
176
|
+
raise typer.Exit(1) from e
|
|
177
|
+
|
|
178
|
+
if no_index:
|
|
179
|
+
# Show next steps
|
|
180
|
+
console.print()
|
|
181
|
+
console.print(f"[bold]Repository '{repo.name}' initialized successfully![/bold]")
|
|
182
|
+
console.print()
|
|
183
|
+
console.print("Next steps:")
|
|
184
|
+
console.print(f" 1. Run [bold]indexter index {repo.name}[/bold] to index the repository.")
|
|
185
|
+
console.print(f" 2. Use [bold]indexter search 'your query' {repo.name}[/bold] to search the indexed code.")
|
|
186
|
+
console.print()
|
|
187
|
+
else:
|
|
188
|
+
# Show next steps and index summary
|
|
189
|
+
console.print()
|
|
190
|
+
console.print(f"[bold]Repository '{repo.name}' initialized and indexed successfully![/bold]")
|
|
191
|
+
if result:
|
|
192
|
+
console.print(f" [green]✓[/green] {repo.name}: {result.summary}")
|
|
193
|
+
console.print()
|
|
194
|
+
console.print("Next steps:")
|
|
195
|
+
console.print(f" 1. Use [bold]indexter search 'your query' {repo.name}[/bold] to search the indexed code.")
|
|
196
|
+
console.print()
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@app.command()
|
|
200
|
+
def index(
|
|
201
|
+
name: Annotated[str, typer.Argument(help="Name of the repository to index")],
|
|
202
|
+
full: Annotated[
|
|
203
|
+
bool,
|
|
204
|
+
typer.Option("--full", "-f", help="Force full re-indexing of the repository", show_default=True),
|
|
205
|
+
] = False,
|
|
206
|
+
) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Index a git repository in the vector store.
|
|
209
|
+
|
|
210
|
+
Performs semantic indexing of the specified git repository, storing code
|
|
211
|
+
snippets as vector embeddings for efficient retrieval.
|
|
212
|
+
|
|
213
|
+
By default, only changed documents are indexed for efficiency. Use --full to
|
|
214
|
+
force complete re-indexing of all documents.
|
|
215
|
+
|
|
216
|
+
The command tracks added, updated, and deleted nodes, and reports any
|
|
217
|
+
errors encountered during the indexing process.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
name: Name of the repository to index. Must be a repository previously
|
|
221
|
+
initialized with 'indexter init'.
|
|
222
|
+
full: If True, forces full re-indexing of all documents in the repository,
|
|
223
|
+
ignoring incremental change detection. Defaults to False.
|
|
224
|
+
|
|
225
|
+
Raises:
|
|
226
|
+
typer.Exit: Exits with code 1 if the repository is not found or if an
|
|
227
|
+
unexpected error occurs.
|
|
228
|
+
|
|
229
|
+
Examples:
|
|
230
|
+
$ indexter index myrepo
|
|
231
|
+
✓ myrepo: +15 ~3 -2 (5 documents synced) (1 documents deleted)
|
|
232
|
+
Indexing complete!
|
|
233
|
+
|
|
234
|
+
$ indexter index myrepo --full
|
|
235
|
+
✓ myrepo: +150 ~0 -0 (50 documents synced) (0 documents deleted)
|
|
236
|
+
Indexing complete!
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
async def _index() -> tuple[Repo, IndexResult]:
|
|
240
|
+
"""Run all index operations in a single event loop."""
|
|
241
|
+
async with VectorStore() as store:
|
|
242
|
+
repo = await Repo.get_one(name)
|
|
243
|
+
result = await repo.index(store, full)
|
|
244
|
+
return repo, result
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
with Progress(
|
|
248
|
+
SpinnerColumn(),
|
|
249
|
+
TextColumn("[progress.description]{task.description}"),
|
|
250
|
+
console=console,
|
|
251
|
+
) as progress:
|
|
252
|
+
progress.add_task("Indexing...", total=None)
|
|
253
|
+
repo, result = cast(tuple[Repo, IndexResult], anyio.run(_index))
|
|
254
|
+
except RepoNotFoundError as e:
|
|
255
|
+
console.print(f"[red]✗[/red] Repository not found: {name}")
|
|
256
|
+
console.print("Run 'indexter init <repo_path>' to initialize the repository first.")
|
|
257
|
+
raise typer.Exit(1) from e
|
|
258
|
+
except Exception as e:
|
|
259
|
+
console.print(f"[red]✗[/red] Unexpected error: {e}")
|
|
260
|
+
raise typer.Exit(1) from e
|
|
261
|
+
|
|
262
|
+
if result.documents_indexed == 0:
|
|
263
|
+
console.print(f" [dim]●[/dim] {repo.name}: up to date")
|
|
264
|
+
console.print(
|
|
265
|
+
f" [green]✓[/green] No changes detected. {result.documents_checked} documents checked. "
|
|
266
|
+
"Repository is up to date."
|
|
267
|
+
)
|
|
268
|
+
else:
|
|
269
|
+
console.print(f" [green]✓[/green] {repo.name}: {result.summary}")
|
|
270
|
+
|
|
271
|
+
if result.errors:
|
|
272
|
+
console.print(f" [yellow]Errors: {len(result.errors)}[/yellow]")
|
|
273
|
+
for error in result.errors[:5]:
|
|
274
|
+
console.print(f" - {error}")
|
|
275
|
+
if len(result.errors) > 5:
|
|
276
|
+
console.print(f" ... and {len(result.errors) - 5} more")
|
|
277
|
+
console.print(" [yellow]Some documents could not be indexed. Please check the errors above.[/yellow]")
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
if result.skipped_documents:
|
|
281
|
+
console.print(f" [yellow]Skipped: {result.skipped_documents} documents[/yellow]")
|
|
282
|
+
console.print(
|
|
283
|
+
" [yellow]Some documents skipped during indexing due to maximum allowed file limit "
|
|
284
|
+
"being exceeded.[/yellow]"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
console.print("[green]Indexing complete![/green]")
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@app.command()
|
|
291
|
+
def search(
|
|
292
|
+
query: Annotated[str, typer.Argument(help="Search query")],
|
|
293
|
+
name: Annotated[str, typer.Argument(help="Name of the repository to search")],
|
|
294
|
+
limit: Annotated[int, typer.Option("--limit", "-l", help="Number of results to return", show_default=True)] = 10,
|
|
295
|
+
) -> None:
|
|
296
|
+
"""Search indexed nodes in a repository.
|
|
297
|
+
|
|
298
|
+
Performs semantic search across the indexed codebase using vector similarity.
|
|
299
|
+
Returns the most relevant code snippets ranked by similarity score, displayed
|
|
300
|
+
in a formatted table with scores, content previews, and file paths.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
query: Natural language search query describing the code you're looking for.
|
|
304
|
+
name: Name of the repository to search. Must be an indexed repository.
|
|
305
|
+
limit: Maximum number of search results to return. Defaults to 10.
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
typer.Exit: Exits with code 1 if the repository is not found or if an
|
|
309
|
+
unexpected error occurs.
|
|
310
|
+
|
|
311
|
+
Examples:
|
|
312
|
+
$ indexter search "authentication middleware" myrepo
|
|
313
|
+
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┓
|
|
314
|
+
┃ Score ┃ Content ┃ Document Path ┃
|
|
315
|
+
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━┩
|
|
316
|
+
│ 0.856 │ def auth │ src/auth/mid... │
|
|
317
|
+
└───────┴──────────────────┴──────────────────┘
|
|
318
|
+
|
|
319
|
+
$ indexter search "error handling" myrepo --limit 5
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
async def _search() -> tuple[Repo, SearchResults]:
|
|
323
|
+
"""Run all search operations in a single event loop."""
|
|
324
|
+
async with VectorStore() as store:
|
|
325
|
+
repo = await Repo.get_one(name)
|
|
326
|
+
results = await repo.search(query, store, limit=limit)
|
|
327
|
+
return repo, results
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
repo, search_results = cast(tuple[Repo, SearchResults], anyio.run(_search))
|
|
331
|
+
except RepoNotFoundError as e:
|
|
332
|
+
console.print(f"[red]✗[/red] Repository not found: {name}")
|
|
333
|
+
raise typer.Exit(1) from e
|
|
334
|
+
except Exception as e:
|
|
335
|
+
console.print(f"[red]✗[/red] Unexpected error: {e}")
|
|
336
|
+
raise typer.Exit(1) from e
|
|
337
|
+
|
|
338
|
+
if not search_results.results:
|
|
339
|
+
console.print(f"[yellow]No results found for query:[/yellow] {query}")
|
|
340
|
+
return
|
|
341
|
+
|
|
342
|
+
table = Table(title=f"Search Results for '{query}' in '{repo.name}'")
|
|
343
|
+
table.add_column("Score", justify="right", style="cyan", no_wrap=True)
|
|
344
|
+
table.add_column("Content", style="magenta")
|
|
345
|
+
table.add_column("Document Path", style="green")
|
|
346
|
+
|
|
347
|
+
for result in search_results.results:
|
|
348
|
+
table.add_row(
|
|
349
|
+
f"{result.score:.4f}",
|
|
350
|
+
result.content.strip().replace("\n", " ")[:50] + "...",
|
|
351
|
+
str(result.metadata.get("document_path", "unknown")),
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
console.print(table)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@app.command()
|
|
358
|
+
def status() -> None:
|
|
359
|
+
"""Show status of indexed repositories.
|
|
360
|
+
|
|
361
|
+
Displays a table of all repositories managed by Indexter, including their
|
|
362
|
+
paths, indexing statistics (number of nodes, documents), and current status.
|
|
363
|
+
This helps track which repositories are indexed and identify those needing
|
|
364
|
+
updates.
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
None: This function prints a formatted table to the console and does
|
|
368
|
+
not return a value.
|
|
369
|
+
|
|
370
|
+
Examples:
|
|
371
|
+
$ indexter status
|
|
372
|
+
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┓
|
|
373
|
+
┃ Name ┃ Path ┃ Nodes ┃ Documents ┃ Stale ┃
|
|
374
|
+
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━┩
|
|
375
|
+
│ myrepo │ /home/user/... │ 1250 │ 45 │ True │
|
|
376
|
+
│ webapp │ /home/user/... │ 3420 │ 128 │ False │
|
|
377
|
+
└─────────┴────────────────┴───────┴───────────┴─────────────┘
|
|
378
|
+
"""
|
|
379
|
+
|
|
380
|
+
async def _status() -> list[Repo]:
|
|
381
|
+
"""Run all status operations in a single event loop."""
|
|
382
|
+
async with VectorStore() as store:
|
|
383
|
+
return await Repo.get_all(store, with_metadata=True)
|
|
384
|
+
|
|
385
|
+
repos = cast(list[Repo], anyio.run(_status))
|
|
386
|
+
|
|
387
|
+
if not repos:
|
|
388
|
+
console.print("[bold]Repositories[/bold]")
|
|
389
|
+
console.print(" No repositories indexed. Run 'indexter index <repo_path>' to index a repository.")
|
|
390
|
+
console.print()
|
|
391
|
+
return
|
|
392
|
+
|
|
393
|
+
table = Table(title="Indexed Repositories")
|
|
394
|
+
table.add_column("Name", style="cyan")
|
|
395
|
+
table.add_column("Path")
|
|
396
|
+
table.add_column("Nodes", justify="right")
|
|
397
|
+
table.add_column("Documents", justify="right")
|
|
398
|
+
table.add_column("Stale", justify="right")
|
|
399
|
+
|
|
400
|
+
for repo in repos:
|
|
401
|
+
table.add_row(
|
|
402
|
+
repo.name,
|
|
403
|
+
str(repo.path),
|
|
404
|
+
str(repo.metadata.nodes_indexed if repo.metadata else "-"),
|
|
405
|
+
str(repo.metadata.documents_indexed if repo.metadata else "-"),
|
|
406
|
+
str(repo.metadata.is_stale if repo.metadata else "-"),
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
console.print(table)
|
|
410
|
+
console.print()
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
@app.command()
|
|
414
|
+
def forget(
|
|
415
|
+
name: Annotated[str, typer.Argument(help="Name of the repository to forget")],
|
|
416
|
+
) -> None:
|
|
417
|
+
"""Forget a repository (remove from indexter and delete indexed data).
|
|
418
|
+
|
|
419
|
+
Removes a repository from Indexter's management and deletes all associated
|
|
420
|
+
indexed data from the vector store. This operation cannot be undone. The
|
|
421
|
+
original repository files remain unchanged.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
name: Name of the repository to remove. Must be a previously initialized
|
|
425
|
+
repository.
|
|
426
|
+
|
|
427
|
+
Raises:
|
|
428
|
+
typer.Exit: Exits with code 1 if the repository is not found or if an
|
|
429
|
+
unexpected error occurs during removal.
|
|
430
|
+
|
|
431
|
+
Examples:
|
|
432
|
+
$ indexter forget myrepo
|
|
433
|
+
✓ Repository 'myrepo' is forgotten.
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
async def _forget() -> None:
|
|
437
|
+
"""Run all forget operations in a single event loop."""
|
|
438
|
+
async with VectorStore() as store:
|
|
439
|
+
await Repo.remove_one(name, store)
|
|
440
|
+
|
|
441
|
+
try:
|
|
442
|
+
anyio.run(_forget)
|
|
443
|
+
except RepoNotFoundError as e:
|
|
444
|
+
console.print(f"[red]✗[/red] Repository not found: {name}")
|
|
445
|
+
raise typer.Exit(1) from e
|
|
446
|
+
except Exception as e:
|
|
447
|
+
console.print(f"[red]✗[/red] Unexpected error: {e}")
|
|
448
|
+
raise typer.Exit(1) from e
|
|
449
|
+
else:
|
|
450
|
+
console.print(f"[green]✓[/green] Repository '{name}' is forgotten.")
|
indexter/cli/config.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration CLI commands.
|
|
3
|
+
|
|
4
|
+
This module provides CLI commands for viewing Indexter's global
|
|
5
|
+
configuration settings. It includes commands to display the configuration file
|
|
6
|
+
contents and retrieve the configuration file path.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.syntax import Syntax
|
|
12
|
+
|
|
13
|
+
from indexter.config import settings
|
|
14
|
+
|
|
15
|
+
config_app = typer.Typer(
|
|
16
|
+
name="config",
|
|
17
|
+
help="View Indexter global settings.",
|
|
18
|
+
no_args_is_help=True,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@config_app.command(name="show")
|
|
25
|
+
def config_show() -> None:
|
|
26
|
+
"""Show Indexter global settings config.
|
|
27
|
+
|
|
28
|
+
Displays the Indexter configuration file path and its contents in a
|
|
29
|
+
formatted view. The configuration file is displayed with syntax
|
|
30
|
+
highlighting using the Monokai theme. If the configuration file
|
|
31
|
+
does not exist, a message is displayed indicating this.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
None: This function prints output to the console and does not
|
|
35
|
+
return a value.
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
$ indexter config show
|
|
39
|
+
Indexter Settings
|
|
40
|
+
Config file: /home/user/.config/indexter/config.toml
|
|
41
|
+
|
|
42
|
+
[config file contents with syntax highlighting]
|
|
43
|
+
"""
|
|
44
|
+
console.print("[bold]Indexter Settings[/bold]")
|
|
45
|
+
console.print(f" Config file: {str(settings.config_file)}", overflow="ignore", crop=False)
|
|
46
|
+
console.print()
|
|
47
|
+
|
|
48
|
+
if settings.config_file.exists():
|
|
49
|
+
content = settings.config_file.read_text()
|
|
50
|
+
syntax = Syntax(content, "toml", theme="monokai", line_numbers=True)
|
|
51
|
+
console.print(syntax)
|
|
52
|
+
else:
|
|
53
|
+
console.print("[dim]Config file not found.[/dim]")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@config_app.command(name="path")
|
|
57
|
+
def config_path() -> None:
|
|
58
|
+
"""Print the path to the Indexter settings config file.
|
|
59
|
+
|
|
60
|
+
Outputs the absolute file system path to the Indexter configuration
|
|
61
|
+
file. This uses plain print() instead of Rich's console.print() to
|
|
62
|
+
avoid any formatting or text wrapping, making the output suitable
|
|
63
|
+
for use in scripts or command substitution.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
None: This function prints output to stdout and does not return
|
|
67
|
+
a value.
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
$ indexter config path
|
|
71
|
+
/home/user/.config/indexter/config.toml
|
|
72
|
+
|
|
73
|
+
# Use in shell scripts
|
|
74
|
+
$ cat $(indexter config path)
|
|
75
|
+
"""
|
|
76
|
+
# Use print instead of console.print to avoid Rich formatting/wrapping
|
|
77
|
+
print(settings.config_file)
|