doclex-cli 0.1.1__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.
Files changed (41) hide show
  1. doclex_cli-0.1.1/.gitignore +14 -0
  2. doclex_cli-0.1.1/CHANGELOG.md +41 -0
  3. doclex_cli-0.1.1/CLAUDE.md +346 -0
  4. doclex_cli-0.1.1/LICENSE +21 -0
  5. doclex_cli-0.1.1/PKG-INFO +221 -0
  6. doclex_cli-0.1.1/PLAN.md +264 -0
  7. doclex_cli-0.1.1/README.es.md +201 -0
  8. doclex_cli-0.1.1/README.md +201 -0
  9. doclex_cli-0.1.1/benchmarks/beir_eval.py +359 -0
  10. doclex_cli-0.1.1/benchmarks/compare.py +103 -0
  11. doclex_cli-0.1.1/pyproject.toml +47 -0
  12. doclex_cli-0.1.1/src/doclex/__init__.py +0 -0
  13. doclex_cli-0.1.1/src/doclex/cache/__init__.py +0 -0
  14. doclex_cli-0.1.1/src/doclex/cache/store.py +196 -0
  15. doclex_cli-0.1.1/src/doclex/cli.py +430 -0
  16. doclex_cli-0.1.1/src/doclex/config.py +43 -0
  17. doclex_cli-0.1.1/src/doclex/extraction/__init__.py +0 -0
  18. doclex_cli-0.1.1/src/doclex/extraction/extractor.py +72 -0
  19. doclex_cli-0.1.1/src/doclex/index/__init__.py +0 -0
  20. doclex_cli-0.1.1/src/doclex/index/bm25_index.py +120 -0
  21. doclex_cli-0.1.1/src/doclex/ingestion/__init__.py +0 -0
  22. doclex_cli-0.1.1/src/doclex/ingestion/scanner.py +87 -0
  23. doclex_cli-0.1.1/src/doclex/ingestion/watcher.py +94 -0
  24. doclex_cli-0.1.1/src/doclex/metadata/__init__.py +0 -0
  25. doclex_cli-0.1.1/src/doclex/metadata/db.py +250 -0
  26. doclex_cli-0.1.1/src/doclex/metadata/models.py +55 -0
  27. doclex_cli-0.1.1/src/doclex/pipeline.py +257 -0
  28. doclex_cli-0.1.1/src/doclex/registry.py +103 -0
  29. doclex_cli-0.1.1/src/doclex/segmentation/__init__.py +0 -0
  30. doclex_cli-0.1.1/src/doclex/segmentation/segmenter.py +398 -0
  31. doclex_cli-0.1.1/tests/__init__.py +0 -0
  32. doclex_cli-0.1.1/tests/conftest.py +80 -0
  33. doclex_cli-0.1.1/tests/test_cache.py +248 -0
  34. doclex_cli-0.1.1/tests/test_cli.py +329 -0
  35. doclex_cli-0.1.1/tests/test_db.py +290 -0
  36. doclex_cli-0.1.1/tests/test_extraction.py +41 -0
  37. doclex_cli-0.1.1/tests/test_index.py +130 -0
  38. doclex_cli-0.1.1/tests/test_registry.py +157 -0
  39. doclex_cli-0.1.1/tests/test_scanner.py +214 -0
  40. doclex_cli-0.1.1/tests/test_segmentation.py +222 -0
  41. doclex_cli-0.1.1/uv.lock +1639 -0
@@ -0,0 +1,14 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .doclex/
5
+ *.db
6
+ dist/
7
+ *.egg-info/
8
+ .env
9
+ ANDE Files/
10
+ .claude/
11
+ .agents/
12
+ skills-lock.json
13
+ benchmarks/data/
14
+ benchmarks/results/
@@ -0,0 +1,41 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [0.1.1] - 2026-08-02
9
+
10
+ ### Fixed
11
+
12
+ - Search result snippets now anchor on the window with the highest density of query-term matches instead of the first occurrence of the first term, so the snippet favors where the query actually clusters rather than an early, isolated mention.
13
+
14
+ ## [0.1.0] - 2026-08-02
15
+
16
+ First release — CLI MVP (Fase 1): local, embeddings-free document search over PDF, DOCX, XLSX, CSV, Markdown and TXT files.
17
+
18
+ ### Added
19
+
20
+ - Local document search CLI (`doclex`) — BM25 lexical search, no embeddings, no vector DB, no cloud.
21
+ - Ingestion pipeline: folder scanning with hash-based change detection, Kreuzberg-based text extraction, structural segmentation per file type (pages, sheets, sections, blocks), BM25X indexing, SQLite metadata store, and a local extraction cache.
22
+ - Real-time incremental updates via Watchdog file monitoring, plus full reconciliation on startup.
23
+ - `doclex add`/`projects`/`remove` — named project registry mapping folder paths to project names, rejecting overlapping/nested folders.
24
+ - `doclex index --project <name> [--file ...] [--force]` — incremental or forced full reindexing, with per-file targeting.
25
+ - `doclex search <query> --project <name> [--top] [--full]` — top-k BM25 search with citations (file, location, score, snippet).
26
+ - `doclex status --project <name> [--file ...] [--all] [--filter]` — corpus/index status, file listing, and per-file inspection.
27
+ - OCR support (`--ocr`) for scanned PDFs, and layout-aware segmentation via Kreuzberg.
28
+ - Recursive character-based splitter keeping segments within a 1024-character limit while preserving semantic boundaries.
29
+
30
+ ### Changed
31
+
32
+ - CLI output migrated to Rich (progress bars, tables) instead of plain text.
33
+ - Segmentation cache replaced with a unified page-index-style tree; split sub-segments now nest under their extracted heading parent.
34
+ - `list`/`reindex` merged into `index`/`status` flags; cache directory renamed from `.cache` to `.doclex`.
35
+ - Layout-aware PDF segmentation made the default (removed the `--precise` flag).
36
+
37
+ ### Fixed
38
+
39
+ - Ghost BM25 index entries left behind after a forced reindex (index and SQLite are now rebuilt fully in sync).
40
+ - `status`/`search` no longer create the index directory before a project has actually been indexed.
41
+ - Search result snippets now tokenize the query the same way BM25X tokenizes documents (splitting on punctuation, not just whitespace) and snap to word boundaries instead of cutting words mid-way.
@@ -0,0 +1,346 @@
1
+ Antes de escribir cualquier código, genera un plan de implementación completo con estructura de carpetas, módulos, schema SQLite y tools MCP. Espera confirmación antes de proceder.
2
+
3
+ ---
4
+
5
+ # Doclex MCP
6
+
7
+ Servidor MCP para búsqueda local dentro de documentos usando **extracción de texto + segmentación estructural + BM25**.
8
+
9
+ El objetivo es permitir que un agente de IA pueda consultar una carpeta local con documentos heterogéneos y recuperar fragmentos relevantes con citas precisas.
10
+
11
+ ---
12
+
13
+ ## Objetivo
14
+
15
+ Construir un servidor MCP que permita buscar información dentro de archivos locales sin usar embeddings, sin base de datos vectorial y sin depender de servicios externos.
16
+
17
+ Debe funcionar sobre una carpeta local configurable y mantener un índice actualizado automáticamente cuando los archivos cambian.
18
+
19
+ ---
20
+
21
+ ## Alcance
22
+
23
+ El sistema debe:
24
+
25
+ * escanear una carpeta local
26
+ * detectar archivos compatibles
27
+ * extraer su contenido a texto
28
+ * segmentar el contenido en unidades buscables
29
+ * indexar esos segmentos con BM25
30
+ * actualizar el índice cuando los archivos cambien
31
+ * exponer herramientas MCP para búsqueda y recuperación de resultados
32
+
33
+ ---
34
+
35
+ ## Tipos de archivo soportados
36
+
37
+ El sistema debe estar diseñado para soportar al menos:
38
+
39
+ * PDF
40
+ * TXT
41
+ * Markdown
42
+ * DOCX
43
+ * XLSX
44
+ * CSV
45
+
46
+ La extracción debe delegarse a Kreuzberg.
47
+
48
+ ---
49
+
50
+ ## Arquitectura general
51
+
52
+ El sistema se compone de cinco partes principales:
53
+
54
+ ### 1. Ingestion Layer
55
+
56
+ Responsable de descubrir archivos dentro de una carpeta y decidir cuándo un archivo debe procesarse o reprocesarse.
57
+
58
+ Debe detectar:
59
+
60
+ * archivos nuevos
61
+ * archivos modificados
62
+ * archivos eliminados
63
+
64
+ Debe ignorar carpetas internas del sistema como `.cache` o equivalentes.
65
+
66
+ ---
67
+
68
+ ### 2. Extraction Layer
69
+
70
+ Responsable de convertir archivos a texto utilizable.
71
+
72
+ Debe usar Kreuzberg como backend de extracción.
73
+
74
+ La salida de esta capa no debe depender directamente del formato original del extractor, sino normalizarse a una estructura interna propia.
75
+
76
+ ---
77
+
78
+ ### 3. Segmentation Layer
79
+
80
+ Responsable de dividir el contenido en unidades buscables.
81
+
82
+ No se debe indexar el archivo completo como una sola unidad, ni usar chunking ciego por tamaño fijo.
83
+
84
+ La segmentación debe seguir la estructura natural del documento según el tipo de archivo:
85
+
86
+ * PDF → página o bloque
87
+ * XLSX → hoja o tabla
88
+ * DOCX → sección o heading
89
+ * TXT / MD → bloque o heading
90
+ * CSV → tabla o fragmento tabular
91
+
92
+ Cada segmento debe conservar referencia clara a su archivo de origen y su ubicación exacta.
93
+
94
+ ---
95
+
96
+ ### 4. Index Layer
97
+
98
+ Responsable de indexar y consultar segmentos usando BM25.
99
+
100
+ Debe usar BM25X como motor principal.
101
+
102
+ La unidad de indexación debe ser el segmento, no el archivo completo.
103
+
104
+ Debe soportar:
105
+
106
+ * agregar segmentos nuevos
107
+ * actualizar segmentos de un archivo modificado
108
+ * eliminar segmentos de un archivo borrado
109
+ * buscar top-k resultados
110
+
111
+ ---
112
+
113
+ ### 5. Metadata Layer
114
+
115
+ Responsable de mantener el estado del corpus y del índice.
116
+
117
+ Debe usar SQLite.
118
+
119
+ Debe almacenar al menos:
120
+
121
+ * ruta del archivo
122
+ * hash del archivo
123
+ * fecha de modificación
124
+ * tamaño
125
+ * tipo MIME
126
+ * estado de indexación
127
+ * relación entre archivo y segmentos indexados
128
+
129
+ Esto se usa para saber qué archivos deben reprocesarse y para mantener consistencia entre disco, caché e índice.
130
+
131
+ ---
132
+
133
+ ## Caché local
134
+
135
+ El sistema debe mantener una caché local derivada del contenido procesado.
136
+
137
+ La caché debe permitir evitar reprocesar archivos sin cambios y facilitar depuración.
138
+
139
+ Debe almacenar por archivo:
140
+
141
+ * texto completo extraído
142
+ * metadatos
143
+ * segmentos generados
144
+
145
+ La caché no debe ser la fuente principal de búsqueda, pero sí la fuente intermedia entre extracción e indexación.
146
+
147
+ ---
148
+
149
+ ## Actualización automática
150
+
151
+ El sistema debe monitorear la carpeta configurada en tiempo real usando Watchdog.
152
+
153
+ Cuando un archivo se crea, modifica o elimina, el sistema debe:
154
+
155
+ 1. detectar el cambio
156
+ 2. aplicar debounce
157
+ 3. revalidar el estado real del archivo
158
+ 4. reprocesar solo lo necesario
159
+ 5. actualizar caché, SQLite e índice BM25
160
+
161
+ Además del monitoreo por eventos, el sistema debe poder ejecutar reconciliación completa al iniciar.
162
+
163
+ ---
164
+
165
+ ## Flujo de indexación
166
+
167
+ ### Al iniciar
168
+
169
+ * escanear la carpeta objetivo
170
+ * comparar archivos actuales con el estado en SQLite
171
+ * detectar nuevos, modificados y eliminados
172
+ * procesar cada cambio
173
+ * construir o actualizar índice
174
+ * activar el monitor de archivos
175
+
176
+ ### Cuando cambia un archivo
177
+
178
+ * recalcular hash y metadatos
179
+ * reextraer contenido
180
+ * regenerar segmentos
181
+ * reemplazar en índice solo los segmentos de ese archivo
182
+ * actualizar caché y manifiesto
183
+
184
+ ---
185
+
186
+ ## Flujo de búsqueda
187
+
188
+ Cuando un agente realiza una consulta:
189
+
190
+ 1. recibir query de texto
191
+ 2. aplicar normalización básica
192
+ 3. ejecutar búsqueda BM25 sobre segmentos
193
+ 4. recuperar top-k resultados
194
+ 5. devolver resultados enriquecidos con metadatos y contexto
195
+
196
+ ---
197
+
198
+ ## Resultado esperado de una búsqueda
199
+
200
+ Cada resultado debe incluir:
201
+
202
+ * texto del segmento encontrado
203
+ * score de relevancia
204
+ * archivo de origen
205
+ * tipo de archivo
206
+ * ubicación interna:
207
+
208
+ * página
209
+ * hoja
210
+ * sección
211
+ * bloque
212
+ * snippet o texto resumido del match
213
+ * identificador estable del segmento
214
+
215
+ ---
216
+
217
+ ## Requisitos de diseño
218
+
219
+ El sistema debe ser:
220
+
221
+ * local-first
222
+ * incremental
223
+ * explicable
224
+ * determinista
225
+ * barato en recursos
226
+ * fácil de depurar
227
+
228
+ Debe priorizar:
229
+
230
+ * precisión léxica
231
+ * trazabilidad
232
+ * simplicidad operacional
233
+
234
+ sobre:
235
+
236
+ * semántica profunda
237
+ * recuperación por embeddings
238
+ * razonamiento generativo durante búsqueda
239
+
240
+ ---
241
+
242
+ ## Lo que no debe hacer
243
+
244
+ El sistema no debe:
245
+
246
+ * usar embeddings
247
+ * usar base de datos vectorial
248
+ * subir archivos a la nube
249
+ * depender de servicios externos para búsqueda
250
+ * hacer OCR por defecto
251
+ * generar respuestas directamente dentro del índice
252
+
253
+ La responsabilidad del sistema es **recuperar evidencia**, no responder preguntas por sí mismo.
254
+
255
+ ---
256
+
257
+ ## Integración MCP
258
+
259
+ El servidor debe exponer herramientas MCP para que un agente pueda al menos:
260
+
261
+ * buscar dentro del corpus
262
+ * listar archivos indexados
263
+ * consultar detalles de un archivo
264
+ * forzar reindexación si es necesario
265
+
266
+ La interfaz MCP debe estar pensada para que un agente pueda hacer workflows tipo:
267
+
268
+ 1. buscar
269
+ 2. leer evidencia
270
+ 3. refinar búsqueda
271
+ 4. responder usando citas
272
+
273
+ ---
274
+
275
+ ## Stack esperado
276
+
277
+ * Python
278
+ * MCP SDK
279
+ * Kreuzberg
280
+ * BM25X
281
+ * Watchdog
282
+ * SQLite
283
+
284
+ ---
285
+
286
+ ## Prioridad de implementación
287
+
288
+ ### Fase 1 — CLI (MVP)
289
+
290
+ El pipeline debe implementarse y verificarse primero como CLI, antes de exponer cualquier interfaz MCP.
291
+
292
+ Primera versión funcional con:
293
+
294
+ * escaneo de carpeta
295
+ * extracción (Kreuzberg)
296
+ * segmentación estructural por tipo de archivo
297
+ * indexación BM25 (BM25X)
298
+ * búsqueda top-k desde línea de comandos
299
+ * actualización incremental (Watchdog)
300
+ * caché local y estado en SQLite
301
+
302
+ La CLI se organiza alrededor de **proyectos**: un proyecto es un nombre registrado que apunta a una carpeta. Toda resolución de carpeta pasa por un proyecto registrado — no existe un flag `--folder` suelto. Esto evita ambigüedad sobre contra qué carpeta se opera y permite detectar carpetas solapadas entre proyectos (rechazadas al registrar).
303
+
304
+ La CLI debe permitir al menos:
305
+
306
+ * `doclex add <nombre> <carpeta>` — registra una carpeta bajo un nombre de proyecto (rechaza nombres duplicados, carpetas inexistentes y carpetas que se solapen con un proyecto ya registrado)
307
+ * `doclex list` — lista los proyectos registrados
308
+ * `doclex remove <nombre>` — elimina un proyecto y purga siempre su caché e índice asociados
309
+ * `doclex index --project <nombre> [--file <archivo> ...] [--force]` — indexa el proyecto de forma incremental, o reindexa archivos puntuales con `--file` (repetible), o fuerza reextracción total con `--force`
310
+ * `doclex search <query> --project <nombre> [--top N] [--full]` — busca y muestra resultados con citas; `<query>` es un argumento posicional
311
+ * `doclex status --project <nombre> [--file <archivo> ...] [--all] [--filter indexed|error|pending]` — muestra estado del corpus, lista archivos indexados (`--all`) o inspecciona archivos específicos
312
+
313
+ `add`/`list`/`remove` son comandos planos, no un subgrupo `project`, ya que `--project` ya se usa como flag en `index`/`search`/`status`. `doclex list` lista **proyectos**, no archivos — para listar archivos de un proyecto se usa `status --all`. No existe un comando `doclex reindex` independiente: queda cubierto por `index --file`.
314
+
315
+ El resultado de `doclex search` debe mostrar por cada resultado: archivo de origen, ubicación (página / hoja / sección), score de relevancia y snippet del texto encontrado.
316
+
317
+ ### Fase 2 — Servidor MCP
318
+
319
+ La capa MCP se agrega únicamente después de que el pipeline de extracción, segmentación e indexación esté verificado y funcionando correctamente vía CLI.
320
+
321
+ Las herramientas MCP son un wrapper sobre el mismo pipeline ya validado.
322
+
323
+ ### Fase 3 — Futuro opcional
324
+
325
+ Posibles mejoras fuera del alcance actual:
326
+
327
+ * OCR opcional
328
+ * filtros avanzados
329
+ * query expansion
330
+ * query rewriting
331
+ * búsqueda multi-paso
332
+ * integración con agentes más complejos
333
+
334
+ ---
335
+
336
+ ## Resumen funcional
337
+
338
+ Doclex MCP debe convertir una carpeta local de documentos en un corpus buscable para agentes, con actualización automática, recuperación precisa y resultados citables.
339
+
340
+ ---
341
+
342
+ ## Notas para Claude Code
343
+
344
+ - Instalar dependencias con uv, no con pip
345
+ - Usar pyproject.toml para gestión del proyecto
346
+ - No crear archivos de código sin antes mostrar el plan completo y recibir confirmación
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EnPaiva_93
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.
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.4
2
+ Name: doclex-cli
3
+ Version: 0.1.1
4
+ Summary: Local document search — BM25 lexical search over PDFs, DOCX, XLSX, CSV, Markdown and TXT files. No embeddings, no vector DB, no cloud.
5
+ Project-URL: Repository, https://github.com/EnPaiva93/doclex-cli
6
+ Author-email: EnPaiva_93 <enpaiva93@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.12
10
+ Requires-Dist: aiosqlite
11
+ Requires-Dist: bm25x
12
+ Requires-Dist: kreuzberg==4.8.2
13
+ Requires-Dist: typer
14
+ Requires-Dist: watchdog
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest; extra == 'dev'
17
+ Requires-Dist: pytest-asyncio; extra == 'dev'
18
+ Requires-Dist: ranx; extra == 'dev'
19
+ Description-Content-Type: text/markdown
20
+
21
+ <h1 align="center">Doclex</h1>
22
+
23
+ <p align="center"><em>English · <a href="README.es.md">Español</a></em></p>
24
+
25
+ <p align="center">
26
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT">
27
+ <img src="https://img.shields.io/badge/python-3.12%2B-blue.svg" alt="Python 3.12+">
28
+ </p>
29
+
30
+ <p align="center">A CLI that turns a local folder of documents into a searchable, citable corpus. Point it at a folder; extraction, segmentation, indexing, and change tracking happen automatically.</p>
31
+
32
+ ## Features
33
+
34
+ Doclex is the orchestration layer that ties them together so you never have to think about the pipeline yourself:
35
+
36
+ - **Plug and play** — point it at a folder. Extraction, structural segmentation, indexing, and change tracking all happen automatically; there's no pipeline to wire up.
37
+ - **SQLite is the source of truth** — every file's hash, mtime, and indexing status live in one place, so Doclex always knows exactly what changed and reprocesses only that.
38
+ - **Local-first** — everything runs on your machine, nothing leaves it. No embeddings, no vector database, no cloud.
39
+ - **Deterministic** — BM25 lexical scoring, no black-box similarity.
40
+ - **Traceable** — every result cites its file, page/sheet/section, and score.
41
+ - **Incremental** — only reprocesses files that actually changed.
42
+
43
+ ## Install
44
+
45
+ Requires Python ≥3.12 and [uv](https://docs.astral.sh/uv/).
46
+
47
+ ```bash
48
+ git clone https://github.com/EnPaiva93/doclex-cli.git
49
+ cd doclex-cli
50
+ uv sync
51
+ ```
52
+
53
+ ## Quickstart
54
+
55
+ ```bash
56
+ # register a folder as a project
57
+ uv run doclex add mydocs ./path/to/documents
58
+
59
+ # build the index
60
+ uv run doclex index --project mydocs
61
+
62
+ # search it
63
+ uv run doclex search "quarterly budget" --project mydocs
64
+ ```
65
+
66
+ ```
67
+ [1] budget_2024.pdf — page 3 (score: 4.812)
68
+ "...the quarterly budget was approved with a 12% increase in..."
69
+
70
+ [2] notes.md — Planning (score: 2.107)
71
+ "...need to revisit the budget assumptions before..."
72
+ ```
73
+
74
+ ## Walkthrough
75
+
76
+ A typical day-to-day flow, from an empty project to keeping it in sync:
77
+
78
+ ```bash
79
+ # 1. register the folder you want to search
80
+ doclex add contracts ~/Documents/legal/contracts
81
+
82
+ # 2. build the index — extracts, segments, and indexes every supported file
83
+ doclex index --project contracts
84
+ ```
85
+ ```
86
+ Processing: 42 new, 0 modified, 0 deleted
87
+ ✓ Done.
88
+ ```
89
+ ```bash
90
+ # 3. search it
91
+ doclex search "termination clause" --project contracts
92
+ ```
93
+ ```
94
+ [1] MSA_2023_AcmeCorp.pdf — Section 9.2 (score: 6.104)
95
+ "...either party may terminate this Agreement upon 30 days written notice..."
96
+
97
+ [2] vendor_agreement_v3.docx — Termination (score: 5.887)
98
+ "...termination for convenience requires 60 days advance notice to..."
99
+ ```
100
+ ```bash
101
+ # 4. check what's indexed
102
+ doclex status --project contracts
103
+ ```
104
+ ```
105
+ Folder ~/Documents/legal/contracts
106
+ Indexed 42 files
107
+ Segments 318
108
+ BM25 index 318 docs
109
+ Disk state up to date
110
+ ```
111
+ ```bash
112
+ # 5. a new contract shows up on disk — reindexing picks it up automatically
113
+ doclex index --project contracts
114
+ ```
115
+ ```
116
+ Processing: 1 new, 0 modified, 0 deleted
117
+ ✓ Done.
118
+ ```
119
+ ```bash
120
+ # 6. see every registered project
121
+ doclex list
122
+ ```
123
+ ```
124
+ Name Folder
125
+ contracts /home/you/Documents/legal/contracts
126
+ ```
127
+ ```bash
128
+ # 7. done with it — deregister and purge its cache/index
129
+ doclex remove contracts
130
+ ```
131
+
132
+ Each project is independent — register as many non-overlapping folders as you need, each with its own cache and index:
133
+
134
+ ```bash
135
+ doclex add hr-policies ~/Documents/hr
136
+ doclex index --project hr-policies
137
+ doclex search "vacation days" --project hr-policies --top 5
138
+ ```
139
+
140
+ ## Projects
141
+
142
+ Every command operates on a **registered project** — a name bound to a folder — instead of a raw `--folder` flag. This keeps folder resolution unambiguous and lets Doclex reject overlapping folders (a subfolder registered as its own project would silently duplicate indexing and caching):
143
+
144
+ ```bash
145
+ doclex add mydocs ./path/to/documents
146
+ doclex add mydocs ./other # error: name already exists
147
+ doclex add sub ./path/to/documents/subdir # error: overlaps with 'mydocs'
148
+ doclex remove mydocs # deregisters and purges its cache/index
149
+ ```
150
+
151
+ ## Commands
152
+
153
+ | Command | Description |
154
+ |---|---|
155
+ | `doclex add <name> <folder>` | Register a folder under a project name |
156
+ | `doclex list` | List registered projects |
157
+ | `doclex remove <name>` | Remove a project and purge its cache/index |
158
+ | `doclex index --project <name>` | Scan and index (incremental by default) |
159
+ | `doclex search <query> --project <name>` | Search and print results with citations |
160
+ | `doclex status --project <name>` | Show corpus/index status |
161
+
162
+ Run `doclex <command> --help` for all flags and examples.
163
+
164
+ ### Flags in practice
165
+
166
+ ```bash
167
+ # force a full re-extraction
168
+ doclex index --project mydocs --force
169
+
170
+ # reindex specific files after editing them
171
+ doclex index --project mydocs --file report.pdf --file notes.docx
172
+
173
+ # more results, full segment text instead of a snippet
174
+ doclex search "risk assessment" --project mydocs --top 20 --full
175
+
176
+ # list every indexed file and its status
177
+ doclex status --project mydocs --all --filter error
178
+ ```
179
+
180
+ ## Architecture
181
+
182
+ ```
183
+ folder → scan/watch → extract (Kreuzberg) → segment → BM25X index
184
+ ↕
185
+ SQLite (files, segments)
186
+ ```
187
+
188
+ | Layer | Responsibility |
189
+ |---|---|
190
+ | **Ingestion** | Scans a project's folder, hashes files to detect new/modified/deleted ones, skips its own cache directory |
191
+ | **Extraction** | Delegates to [Kreuzberg](https://github.com/Goldziher/kreuzberg) to turn each file into plain text, normalized to an internal format |
192
+ | **Segmentation** | Splits text along the file's natural structure — never blind fixed-size chunking (PDF → page, XLSX → sheet, DOCX → section, TXT/MD → block) |
193
+ | **Index** | Indexes segments (not whole files) with BM25X; supports incremental add/update/delete |
194
+ | **Metadata** | SQLite tracks each file's hash, mtime, size, MIME type, and indexing status, plus which segments belong to it |
195
+
196
+ A local cache stores extracted text and generated segments per file, so unchanged files are never reprocessed.
197
+
198
+ ## Supported file types
199
+
200
+ PDF · TXT · Markdown · DOCX · XLSX · CSV
201
+
202
+ ## Design principles
203
+
204
+ Doclex is built to be local-first, incremental, explainable, deterministic, and cheap to run. It deliberately does **not**:
205
+
206
+ - use embeddings or a vector database
207
+ - upload files anywhere
208
+ - depend on external services to search
209
+ - run OCR by default
210
+ - generate answers — it retrieves evidence, it doesn't interpret it
211
+
212
+ ## Stack
213
+
214
+ Python · [Typer](https://typer.tiangolo.com/) · [Kreuzberg](https://github.com/Goldziher/kreuzberg) · BM25X · Watchdog · SQLite
215
+
216
+ ## Development
217
+
218
+ ```bash
219
+ uv sync --extra dev
220
+ uv run pytest
221
+ ```