trailhead 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.
Files changed (65) hide show
  1. trailhead-0.1.0/LICENSE +21 -0
  2. trailhead-0.1.0/PKG-INFO +520 -0
  3. trailhead-0.1.0/README.md +428 -0
  4. trailhead-0.1.0/pyproject.toml +91 -0
  5. trailhead-0.1.0/setup.cfg +4 -0
  6. trailhead-0.1.0/src/tests/conftest.py +12 -0
  7. trailhead-0.1.0/src/tests/test_config.py +133 -0
  8. trailhead-0.1.0/src/tests/test_embeddings.py +257 -0
  9. trailhead-0.1.0/src/tests/test_indexing.py +631 -0
  10. trailhead-0.1.0/src/tests/test_live_indexer.py +160 -0
  11. trailhead-0.1.0/src/tests/test_query.py +253 -0
  12. trailhead-0.1.0/src/tests/test_server.py +457 -0
  13. trailhead-0.1.0/src/tests/test_smoke.py +38 -0
  14. trailhead-0.1.0/src/trailhead/__init__.py +4 -0
  15. trailhead-0.1.0/src/trailhead/__main__.py +5 -0
  16. trailhead-0.1.0/src/trailhead/cli/__init__.py +3 -0
  17. trailhead-0.1.0/src/trailhead/cli/__main__.py +5 -0
  18. trailhead-0.1.0/src/trailhead/cli/app.py +28 -0
  19. trailhead-0.1.0/src/trailhead/cli/commands/__init__.py +1 -0
  20. trailhead-0.1.0/src/trailhead/cli/commands/embed.py +58 -0
  21. trailhead-0.1.0/src/trailhead/cli/commands/index.py +243 -0
  22. trailhead-0.1.0/src/trailhead/cli/commands/query.py +198 -0
  23. trailhead-0.1.0/src/trailhead/cli/commands/serve.py +158 -0
  24. trailhead-0.1.0/src/trailhead/server/__init__.py +5 -0
  25. trailhead-0.1.0/src/trailhead/server/__main__.py +3 -0
  26. trailhead-0.1.0/src/trailhead/server/app.py +348 -0
  27. trailhead-0.1.0/src/trailhead/server/rate_limit.py +51 -0
  28. trailhead-0.1.0/src/trailhead/services/__init__.py +1 -0
  29. trailhead-0.1.0/src/trailhead/services/config/__init__.py +7 -0
  30. trailhead-0.1.0/src/trailhead/services/config/cache.py +10 -0
  31. trailhead-0.1.0/src/trailhead/services/config/models.py +31 -0
  32. trailhead-0.1.0/src/trailhead/services/embeddings/__init__.py +17 -0
  33. trailhead-0.1.0/src/trailhead/services/embeddings/generator.py +44 -0
  34. trailhead-0.1.0/src/trailhead/services/embeddings/model_store.py +44 -0
  35. trailhead-0.1.0/src/trailhead/services/indexing/__init__.py +33 -0
  36. trailhead-0.1.0/src/trailhead/services/indexing/adapters/__init__.py +87 -0
  37. trailhead-0.1.0/src/trailhead/services/indexing/adapters/base.py +162 -0
  38. trailhead-0.1.0/src/trailhead/services/indexing/adapters/bash.py +97 -0
  39. trailhead-0.1.0/src/trailhead/services/indexing/adapters/c.py +183 -0
  40. trailhead-0.1.0/src/trailhead/services/indexing/adapters/cpp.py +178 -0
  41. trailhead-0.1.0/src/trailhead/services/indexing/adapters/csharp.py +196 -0
  42. trailhead-0.1.0/src/trailhead/services/indexing/adapters/go.py +243 -0
  43. trailhead-0.1.0/src/trailhead/services/indexing/adapters/html.py +176 -0
  44. trailhead-0.1.0/src/trailhead/services/indexing/adapters/java.py +180 -0
  45. trailhead-0.1.0/src/trailhead/services/indexing/adapters/javascript.py +220 -0
  46. trailhead-0.1.0/src/trailhead/services/indexing/adapters/php.py +219 -0
  47. trailhead-0.1.0/src/trailhead/services/indexing/adapters/python.py +374 -0
  48. trailhead-0.1.0/src/trailhead/services/indexing/adapters/registry.py +46 -0
  49. trailhead-0.1.0/src/trailhead/services/indexing/adapters/ruby.py +179 -0
  50. trailhead-0.1.0/src/trailhead/services/indexing/adapters/rust.py +229 -0
  51. trailhead-0.1.0/src/trailhead/services/indexing/adapters/typescript.py +283 -0
  52. trailhead-0.1.0/src/trailhead/services/indexing/graph.py +110 -0
  53. trailhead-0.1.0/src/trailhead/services/indexing/graph_query.py +176 -0
  54. trailhead-0.1.0/src/trailhead/services/indexing/live_indexer.py +195 -0
  55. trailhead-0.1.0/src/trailhead/services/indexing/parser.py +4 -0
  56. trailhead-0.1.0/src/trailhead/services/indexing/query.py +112 -0
  57. trailhead-0.1.0/src/trailhead/services/indexing/query_templates.py +554 -0
  58. trailhead-0.1.0/src/trailhead/services/indexing/sqlite_store.py +542 -0
  59. trailhead-0.1.0/src/trailhead/services/indexing/walker.py +45 -0
  60. trailhead-0.1.0/src/trailhead.egg-info/PKG-INFO +520 -0
  61. trailhead-0.1.0/src/trailhead.egg-info/SOURCES.txt +63 -0
  62. trailhead-0.1.0/src/trailhead.egg-info/dependency_links.txt +1 -0
  63. trailhead-0.1.0/src/trailhead.egg-info/entry_points.txt +2 -0
  64. trailhead-0.1.0/src/trailhead.egg-info/requires.txt +61 -0
  65. trailhead-0.1.0/src/trailhead.egg-info/top_level.txt +2 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cliff Bressette
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,520 @@
1
+ Metadata-Version: 2.4
2
+ Name: trailhead
3
+ Version: 0.1.0
4
+ Summary: Codebase indexing and semantic search using tree-sitter parsing, vector embeddings, and SQLite graph storage.
5
+ Author-email: Cliff Bressette <cliffordbressette@mcindi.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Cliff Bressette
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/McIndi/trailhead
29
+ Project-URL: Repository, https://github.com/McIndi/trailhead
30
+ Project-URL: Issues, https://github.com/McIndi/trailhead/issues
31
+ Keywords: indexing,semantic-search,rag,sqlite,vectors,tree-sitter,embeddings,code-search
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.10
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
40
+ Classifier: Topic :: Text Processing :: Indexing
41
+ Requires-Python: >=3.10
42
+ Description-Content-Type: text/markdown
43
+ License-File: LICENSE
44
+ Requires-Dist: sentence-transformers
45
+ Requires-Dist: tree-sitter>=0.21
46
+ Requires-Dist: tree-sitter-python>=0.21
47
+ Requires-Dist: sqliteai-vector
48
+ Requires-Dist: fastapi>=0.115
49
+ Requires-Dist: uvicorn>=0.30
50
+ Requires-Dist: watchfiles>=0.24
51
+ Provides-Extra: dev
52
+ Requires-Dist: pytest>=8.0; extra == "dev"
53
+ Requires-Dist: httpx>=0.27; extra == "dev"
54
+ Provides-Extra: javascript
55
+ Requires-Dist: tree-sitter-javascript>=0.21; extra == "javascript"
56
+ Provides-Extra: typescript
57
+ Requires-Dist: tree-sitter-typescript>=0.21; extra == "typescript"
58
+ Provides-Extra: rust
59
+ Requires-Dist: tree-sitter-rust>=0.21; extra == "rust"
60
+ Provides-Extra: go
61
+ Requires-Dist: tree-sitter-go>=0.21; extra == "go"
62
+ Provides-Extra: java
63
+ Requires-Dist: tree-sitter-java>=0.21; extra == "java"
64
+ Provides-Extra: csharp
65
+ Requires-Dist: tree-sitter-c-sharp>=0.21; extra == "csharp"
66
+ Provides-Extra: c
67
+ Requires-Dist: tree-sitter-c>=0.21; extra == "c"
68
+ Provides-Extra: cpp
69
+ Requires-Dist: tree-sitter-cpp>=0.21; extra == "cpp"
70
+ Provides-Extra: ruby
71
+ Requires-Dist: tree-sitter-ruby>=0.21; extra == "ruby"
72
+ Provides-Extra: php
73
+ Requires-Dist: tree-sitter-php>=0.21; extra == "php"
74
+ Provides-Extra: bash
75
+ Requires-Dist: tree-sitter-bash>=0.21; extra == "bash"
76
+ Provides-Extra: html
77
+ Requires-Dist: tree-sitter-html>=0.21; extra == "html"
78
+ Provides-Extra: all-languages
79
+ Requires-Dist: tree-sitter-javascript>=0.21; extra == "all-languages"
80
+ Requires-Dist: tree-sitter-typescript>=0.21; extra == "all-languages"
81
+ Requires-Dist: tree-sitter-rust>=0.21; extra == "all-languages"
82
+ Requires-Dist: tree-sitter-go>=0.21; extra == "all-languages"
83
+ Requires-Dist: tree-sitter-java>=0.21; extra == "all-languages"
84
+ Requires-Dist: tree-sitter-c-sharp>=0.21; extra == "all-languages"
85
+ Requires-Dist: tree-sitter-c>=0.21; extra == "all-languages"
86
+ Requires-Dist: tree-sitter-cpp>=0.21; extra == "all-languages"
87
+ Requires-Dist: tree-sitter-ruby>=0.21; extra == "all-languages"
88
+ Requires-Dist: tree-sitter-php>=0.21; extra == "all-languages"
89
+ Requires-Dist: tree-sitter-bash>=0.21; extra == "all-languages"
90
+ Requires-Dist: tree-sitter-html>=0.21; extra == "all-languages"
91
+ Dynamic: license-file
92
+
93
+ # trailhead
94
+
95
+ Command-line code indexing and semantic search tool. It parses source files into a **property graph** (modules, classes, functions, and their relationships) stored in SQLite, generates text embeddings with sentence-transformers, and exposes everything through a CLI and HTTP API.
96
+
97
+ - Single CLI command: `th`
98
+ - Text embeddings powered by sentence-transformers (models cached locally)
99
+ - Polyglot code indexing via tree-sitter (Python built-in; 12 additional languages optional)
100
+ - Property graph persisted in a single SQLite file with optional vector search
101
+ - Warm-model FastAPI server keeps the embedding model loaded in memory
102
+ - Background file watcher incrementally re-indexes on change
103
+ - Interactive browser UI for querying and visualizing the code graph
104
+
105
+ ## Requirements
106
+
107
+ - Python 3.10+
108
+
109
+ ## Install
110
+
111
+ Create and activate a virtual environment, then install the project in editable mode with dev dependencies:
112
+
113
+ ```powershell
114
+ python -m venv .venv
115
+ .\.venv\Scripts\Activate.ps1
116
+ python -m pip install --upgrade pip
117
+ python -m pip install -e .[dev]
118
+ ```
119
+
120
+ ### Language support
121
+
122
+ Python is supported out of the box. Additional languages are installed as optional extras. Only the packages you install will be active; missing ones are silently skipped at startup.
123
+
124
+ Install individual languages:
125
+
126
+ ```powershell
127
+ pip install -e .[javascript]
128
+ pip install -e .[typescript]
129
+ pip install -e .[rust]
130
+ pip install -e .[go]
131
+ pip install -e .[java]
132
+ pip install -e .[csharp]
133
+ pip install -e .[c]
134
+ pip install -e .[cpp]
135
+ pip install -e .[ruby]
136
+ pip install -e .[php]
137
+ pip install -e .[bash]
138
+ pip install -e .[html]
139
+ ```
140
+
141
+ Or install everything at once:
142
+
143
+ ```powershell
144
+ pip install -e .[all-languages]
145
+ ```
146
+
147
+ | Extra | Language | File extensions |
148
+ |-------|----------|-----------------|
149
+ | `python` *(built-in)* | Python | `.py` |
150
+ | `javascript` | JavaScript | `.js` `.mjs` `.cjs` |
151
+ | `typescript` | TypeScript / TSX | `.ts` `.tsx` |
152
+ | `rust` | Rust | `.rs` |
153
+ | `go` | Go | `.go` |
154
+ | `java` | Java | `.java` |
155
+ | `csharp` | C# | `.cs` |
156
+ | `c` | C | `.c` `.h` |
157
+ | `cpp` | C++ | `.cpp` `.cc` `.cxx` `.hpp` `.hxx` `.h++` |
158
+ | `ruby` | Ruby | `.rb` |
159
+ | `php` | PHP | `.php` |
160
+ | `bash` | Bash / Shell | `.sh` `.bash` |
161
+ | `html` | HTML | `.html` `.htm` |
162
+
163
+ ## Quick start
164
+
165
+ The typical workflow is: index your source tree once, then serve and query it.
166
+
167
+ ```powershell
168
+ # 1. Index a project (writes .cindex/db.sqlite by default)
169
+ th index . --sqlite-db ./.cindex/graph.db --embed-model sentence-transformers/all-MiniLM-L6-v2
170
+
171
+ # 2. Start the server (watches for changes, keeps embeddings warm)
172
+ th serve . --sqlite-db ./.cindex/graph.db --model sentence-transformers/all-MiniLM-L6-v2
173
+
174
+ # 3. Open the browser UI
175
+ start http://localhost:8000
176
+
177
+ # 4. Or query from the CLI or another terminal
178
+ th query similar "HTTP route registration"
179
+ curl "http://localhost:8000/api/query/similar?text=HTTP+route+registration"
180
+ ```
181
+
182
+ The server re-indexes changed files automatically in the background. You do not need to re-run `th index` while the server is running.
183
+
184
+ ## Usage
185
+
186
+ ### embed
187
+
188
+ Generate an embedding for a piece of text:
189
+
190
+ ```powershell
191
+ th embed "A short sentence to embed"
192
+ th embed "A short sentence to embed" --model sentence-transformers/all-mpnet-base-v2
193
+ ```
194
+
195
+ The command prints the embedding as a JSON array of floats.
196
+
197
+ Optional cache override:
198
+
199
+ ```powershell
200
+ $env:CINDEX_CACHE_DIR = "C:\models\cache"
201
+ th embed "A short sentence to embed"
202
+ th embed "A short sentence to embed" --cache-dir "C:\another\cache"
203
+ ```
204
+
205
+ ### index
206
+
207
+ Index a directory of source files. The graph is persisted to `.cindex/db.sqlite` by default (smart sync: full build on first run, incremental on subsequent runs):
208
+
209
+ ```powershell
210
+ th index .
211
+ ```
212
+
213
+ Use `--in-memory` to build the graph without writing to disk and print a summary:
214
+
215
+ ```powershell
216
+ th index . --in-memory
217
+ th index . --in-memory --output json
218
+ ```
219
+
220
+ Watch for file changes and reindex incrementally (Ctrl-C to stop):
221
+
222
+ ```powershell
223
+ th index . --watch
224
+ th index . --sqlite-db ./.cindex/graph.db --embed-model sentence-transformers/all-MiniLM-L6-v2 --watch
225
+ ```
226
+
227
+ Use a custom database path or add embeddings:
228
+
229
+ ```powershell
230
+ th index . --sqlite-db ./.cindex/graph.db
231
+ th index . --sqlite-db ./.cindex/graph.db --embed-model sentence-transformers/all-MiniLM-L6-v2
232
+ th index . --sqlite-db ./.cindex/graph.db --embed-model sentence-transformers/all-MiniLM-L6-v2 --embed-cache-dir C:\models\cache
233
+ ```
234
+
235
+ When `sqlite-vector` can be loaded, trailhead also initializes vector search for the `vertex_embeddings.embedding` column. If extension loading is unavailable on your platform build, embeddings are still stored as Float32 BLOBs in SQLite.
236
+
237
+ ### serve
238
+
239
+ Run the warm-model API server with a background indexer. The server watches the source tree, keeps the SQLite graph fresh, and reuses the loaded embedding model across index updates. The database defaults to `.cindex/db.sqlite` under the watched directory:
240
+
241
+ ```powershell
242
+ th serve .
243
+ th serve . --model sentence-transformers/all-MiniLM-L6-v2
244
+ th serve . --sqlite-db ./.cindex/graph.db --model sentence-transformers/all-MiniLM-L6-v2
245
+ ```
246
+
247
+ The browser UI is available at `http://localhost:8000` once the server starts.
248
+
249
+ ### query
250
+
251
+ Run a read-only SQL query against the SQLite database (defaults to `./.cindex/db.sqlite`):
252
+
253
+ ```powershell
254
+ th query sql --sql "SELECT label, COUNT(*) AS n FROM vertices GROUP BY label ORDER BY label"
255
+ th query sql --sqlite-db ./.cindex/graph.db --sql "SELECT label, COUNT(*) AS n FROM vertices GROUP BY label ORDER BY label"
256
+ ```
257
+
258
+ Run a semantic similarity query against stored vertex embeddings:
259
+
260
+ ```powershell
261
+ th query similar "find sqlite vector initialization code"
262
+ th query similar "find sqlite vector initialization code" --sqlite-db ./.cindex/graph.db
263
+ th query similar "graph persistence" --sqlite-db ./.cindex/graph.db --label function --k 5 --output json
264
+ ```
265
+
266
+ ## HTTP API
267
+
268
+ When the server is running, the full API schema is available at:
269
+
270
+ ```
271
+ http://localhost:8000/openapi.json
272
+ http://localhost:8000/docs
273
+ ```
274
+
275
+ The schema documents every endpoint, parameter name, type, default, and constraint. **Check it first** before probing endpoints manually.
276
+
277
+ ### Endpoints
278
+
279
+ | Method | Path | Description |
280
+ |--------|------|-------------|
281
+ | `GET` | `/` | Browser UI |
282
+ | `GET` | `/api/health` | Server status and configuration |
283
+ | `POST` | `/api/embed` | Embed a single text string |
284
+ | `POST` | `/api/embed/batch` | Embed multiple texts |
285
+ | `POST` | `/api/query/sql` | Run a read-only SQL query |
286
+ | `GET` | `/api/query/templates` | List built-in query templates |
287
+ | `GET` | `/api/query/templates/{name}` | Get a template's SQL |
288
+ | `POST` | `/api/query/templates/{name}/run` | Run a template against the database |
289
+ | `GET` | `/api/query/similar` | Semantic similarity search (parameter: `text`, `k`) |
290
+ | `GET` | `/api/graph/vertices` | Search vertices by name, label, or path |
291
+ | `GET` | `/api/graph/traverse` | Traverse the graph from a vertex |
292
+
293
+ ### SQL schema
294
+
295
+ The two core tables are:
296
+
297
+ **`vertices`** — one row per code symbol:
298
+
299
+ | Column | Type | Notes |
300
+ |--------|------|-------|
301
+ | `id` | TEXT | UUID, used for graph traversal |
302
+ | `label` | TEXT | `module`, `class`, `function`, `external` |
303
+ | `name` | TEXT | Symbol name |
304
+ | `path` | TEXT | Absolute file path |
305
+ | `line` | INTEGER | Line number (null for modules) |
306
+ | `complexity` | INTEGER | McCabe complexity (functions only) |
307
+ | `properties_json` | TEXT | JSON blob with `source`, `docstring`, and all other properties |
308
+
309
+ **`edges`** — relationships between vertices:
310
+
311
+ | Column | Type | Notes |
312
+ |--------|------|-------|
313
+ | `id` | TEXT | UUID |
314
+ | `label` | TEXT | `defines`, `has_method`, `imports`, `calls` |
315
+ | `out_v_id` | TEXT | Source vertex id |
316
+ | `in_v_id` | TEXT | Target vertex id |
317
+ | `properties_json` | TEXT | Always `{}` currently |
318
+
319
+ Edge labels and their meaning:
320
+
321
+ | Label | Meaning |
322
+ |-------|---------|
323
+ | `defines` | Module → class or function it defines |
324
+ | `has_method` | Class → method |
325
+ | `imports` | Module → external symbol it imports |
326
+ | `calls` | Function → function it calls |
327
+
328
+ `source` and `docstring` live inside `properties_json` rather than as top-level columns. To filter on them in SQL, use `json_extract`:
329
+
330
+ ```sql
331
+ -- Functions whose source mentions "HTTPException"
332
+ SELECT name, path, line
333
+ FROM vertices
334
+ WHERE label = 'function'
335
+ AND json_extract(properties_json, '$.source') LIKE '%HTTPException%'
336
+
337
+ -- Functions with a docstring
338
+ SELECT name, path
339
+ FROM vertices
340
+ WHERE label = 'function'
341
+ AND json_extract(properties_json, '$.docstring') IS NOT NULL
342
+ ```
343
+
344
+ ### HTTP query examples
345
+
346
+ ```powershell
347
+ # Health check
348
+ curl http://localhost:8000/api/health
349
+
350
+ # Embed text
351
+ curl -X POST http://localhost:8000/api/embed \
352
+ -H "Content-Type: application/json" \
353
+ -d '{"text": "hello world"}'
354
+
355
+ # Semantic search — note the parameter is "k", not "limit"
356
+ curl "http://localhost:8000/api/query/similar?text=route+registration&k=5"
357
+
358
+ # Filter semantic search to functions only
359
+ curl "http://localhost:8000/api/query/similar?text=route+registration&k=5&label=function"
360
+
361
+ # SQL query
362
+ curl -X POST http://localhost:8000/api/query/sql \
363
+ -H "Content-Type: application/json" \
364
+ -d '{"sql": "SELECT label, COUNT(*) AS n FROM vertices GROUP BY label"}'
365
+
366
+ # Find a vertex by name, then get its id for traversal
367
+ curl "http://localhost:8000/api/graph/vertices?name=ui_dashboard&label=function"
368
+
369
+ # Traverse outward along call edges only (shows what a function calls)
370
+ curl "http://localhost:8000/api/graph/traverse?vertex_id=<id>&direction=out&depth=2&edge_labels=calls"
371
+
372
+ # Traverse inward along call edges only (shows what calls a function)
373
+ curl "http://localhost:8000/api/graph/traverse?vertex_id=<id>&direction=in&depth=2&edge_labels=calls"
374
+
375
+ # Run a built-in query template
376
+ curl http://localhost:8000/api/query/templates
377
+ curl -X POST http://localhost:8000/api/query/templates/function_complexity/run
378
+ ```
379
+
380
+ ### Built-in query templates
381
+
382
+ Templates are pre-built SQL queries runnable without writing any SQL. Categories:
383
+
384
+ | Category | Templates |
385
+ |----------|-----------|
386
+ | `quality` | `function_complexity`, `missing_docstrings`, `undocumented_public_api`, `todo_fixme_inventory` |
387
+ | `testing` | `symbols_not_represented_by_tests`, `test_coverage_ratio_by_file`, `largest_untested_symbols` |
388
+ | `architecture` | `duplicate_symbol_names`, `dependency_hotspots`, `external_dependency_pressure` |
389
+ | `calls` | `most_called_functions`, `call_graph_hubs` |
390
+ | `data_health` | `missing_source_for_functions`, `orphan_edges` |
391
+
392
+ ### Typical workflow for code exploration
393
+
394
+ 1. **Find a starting point** — use semantic search or `/api/graph/vertices?name=...` to locate a vertex and grab its `id`.
395
+ 2. **Understand its call chain** — traverse outward with `edge_labels=calls` to see what it calls; inward to see its callers.
396
+ 3. **Understand its structure** — traverse with `edge_labels=defines,has_method` to see what a module or class contains.
397
+ 4. **Run quality checks** — use the built-in templates for complexity, missing docs, or dependency hotspots without writing SQL.
398
+ 5. **Ad-hoc queries** — use `/api/query/sql` with `json_extract` to filter on source content, docstrings, or any property.
399
+
400
+ ## Tests
401
+
402
+ ```powershell
403
+ pytest
404
+ ```
405
+
406
+ ## Project Layout
407
+
408
+ ```text
409
+ .
410
+ |-- pyproject.toml
411
+ |-- README.md
412
+ |-- src/
413
+ | `-- cindex/
414
+ | |-- __init__.py
415
+ | |-- __main__.py
416
+ | |-- cli/
417
+ | | |-- __init__.py
418
+ | | |-- __main__.py
419
+ | | |-- app.py
420
+ | | `-- commands/
421
+ | | |-- __init__.py
422
+ | | |-- embed.py
423
+ | | |-- index.py
424
+ | | |-- query.py
425
+ | | `-- serve.py
426
+ | |-- server/
427
+ | | |-- __init__.py
428
+ | | |-- __main__.py
429
+ | | |-- app.py
430
+ | | `-- templates/
431
+ | | `-- query_ui.html
432
+ | `-- services/
433
+ | |-- config/
434
+ | | `-- cache.py
435
+ | |-- indexing/
436
+ | | |-- __init__.py
437
+ | | |-- graph.py
438
+ | | |-- graph_query.py
439
+ | | |-- live_indexer.py
440
+ | | |-- parser.py # re-exports parse_python_file (backwards compat)
441
+ | | |-- query.py
442
+ | | |-- sqlite_store.py
443
+ | | |-- walker.py
444
+ | | `-- adapters/ # language adapter registry
445
+ | | |-- __init__.py # auto-registers available adapters
446
+ | | |-- base.py # LanguageAdapter ABC + shared utilities
447
+ | | |-- registry.py # extension → adapter map, parse_file()
448
+ | | |-- python.py # Python (built-in)
449
+ | | |-- javascript.py # JavaScript (optional)
450
+ | | |-- typescript.py # TypeScript / TSX (optional)
451
+ | | |-- rust.py # Rust (optional)
452
+ | | |-- go.py # Go (optional)
453
+ | | |-- java.py # Java (optional)
454
+ | | |-- csharp.py # C# (optional)
455
+ | | |-- c.py # C (optional)
456
+ | | |-- cpp.py # C++ (optional)
457
+ | | |-- ruby.py # Ruby (optional)
458
+ | | |-- php.py # PHP (optional)
459
+ | | |-- bash.py # Bash / Shell (optional)
460
+ | | `-- html.py # HTML (optional)
461
+ | `-- embeddings/
462
+ | |-- generator.py
463
+ | `-- model_store.py
464
+ `-- tests/
465
+ |-- conftest.py
466
+ |-- test_indexing.py
467
+ |-- test_query.py
468
+ |-- test_server.py
469
+ `-- test_smoke.py
470
+ ```
471
+
472
+ ## Adding a custom language adapter
473
+
474
+ Any language with a tree-sitter Python binding can be supported in three steps:
475
+
476
+ ```python
477
+ # 1. Create your adapter (e.g. my_adapters/kotlin.py)
478
+ from cindex.services.indexing.adapters.base import LanguageAdapter, _node_text, _complexity
479
+ from cindex.services.indexing.graph import PropertyGraph, Vertex
480
+ from pathlib import Path
481
+
482
+ class KotlinAdapter(LanguageAdapter):
483
+ extensions = frozenset({".kt", ".kts"})
484
+
485
+ @classmethod
486
+ def is_available(cls) -> bool:
487
+ try:
488
+ import tree_sitter_kotlin # noqa: F401
489
+ return True
490
+ except ImportError:
491
+ return False
492
+
493
+ def parse(self, path: Path, graph: PropertyGraph) -> Vertex:
494
+ import tree_sitter_kotlin as tskotlin
495
+ from tree_sitter import Language, Parser
496
+ source = path.read_bytes()
497
+ language = Language(tskotlin.language())
498
+ parser = Parser(language)
499
+ tree = parser.parse(source)
500
+ module_v = graph.add_vertex("module", name=path.stem, path=str(path))
501
+ # ... walk tree and add vertices/edges ...
502
+ return module_v
503
+
504
+ # 2. Register it at startup (e.g. in your app's __init__ or conftest)
505
+ from cindex.services.indexing.adapters import register
506
+ register(KotlinAdapter())
507
+
508
+ # 3. Done — th index, serve, and query all pick it up automatically.
509
+ ```
510
+
511
+ What each adapter should produce:
512
+
513
+ | Vertex label | Meaning | Required properties |
514
+ |---|---|---|
515
+ | `module` | one per source file | `name`, `path` |
516
+ | `class` | class / struct / interface / trait | `name`, `path`, `line` |
517
+ | `function` | function / method | `name`, `path`, `line`, `source`, `complexity` |
518
+ | `external` | imported module name | `name` |
519
+
520
+ Edges: `defines` (module→class, module→function), `has_method` (class→function), `imports` (module→external), `calls` (function→function).