@ninjaxtools/slopdex 0.3.0 → 0.8.0

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.
package/README.md CHANGED
@@ -1,145 +1,257 @@
1
1
  # slopdex
2
2
 
3
- Callable-level embedding index, semantic search, and duplicate discovery for TypeScript and JavaScript repositories.
3
+ Slopdex uses Tree-sitter to index named functions in Python, JavaScript, JSX, TypeScript, TSX, Rust, Go, Java, and C repositories. It uses embeddings to search code by meaning, find similar implementations, and measure whether related functions are stored near each other.
4
4
 
5
- The index extracts named functions with tree-sitter, stores metadata and float32 embeddings in SQLite, and uses `sqlite-vec` for exact cosine search. It supports explicit working-tree updates, transactional Git-delta updates, and function-to-function cross-search within one codebase or between compatible indexes.
5
+ ## Install
6
6
 
7
- ## Requirements
7
+ ```bash
8
+ npm install -g @ninjaxtools/slopdex
9
+ ```
8
10
 
9
- - Node.js 24 or newer
10
- - Git for Git-tracked updates and `added-since` searches
11
- - An OpenAI or Jina AI API key
11
+ ## Getting Started
12
12
 
13
- ## Install
13
+ Set your OpenAI API key:
14
14
 
15
15
  ```bash
16
- npm install
17
- npm run build
16
+ export OPENAI_API_KEY="your-api-key"
18
17
  ```
19
18
 
20
- Install the bundled Slopdex skill for OpenCode:
19
+ Run a semantic search from the repository you want to analyze:
21
20
 
22
21
  ```bash
23
- npm run install:skill:opencode
22
+ slopdex search "validate an authenticated session" --format summary --limit 10
24
23
  ```
25
24
 
26
- This copies the skill to `~/.config/opencode/skills/slopdex/SKILL.md`, creating the destination directories when needed.
25
+ ## Analysis Examples
27
26
 
28
- ## Configuration
27
+ ### Search By Function Purpose
29
28
 
30
- Create `.slopdex/config.json` in the repository being indexed:
29
+ Enable optional purpose summaries for every indexed callable:
31
30
 
32
- ```json
33
- {
34
- "provider": "jina",
35
- "model": "jina-embeddings-v4",
36
- "dimensions": 1024,
37
- "exclude": ["**/fixtures/**"]
38
- }
31
+ ```bash
32
+ slopdex use-summaries
33
+ slopdex search-summary "keep the repository index synchronized" --limit 10 --format summary
39
34
  ```
40
35
 
41
- Use `JINA_API_KEY` for Jina AI or `OPENAI_API_KEY` for OpenAI. The OpenAI default is `text-embedding-3-small` with 1536 dimensions. Provider, model, dimensions, and embedding strategy form an immutable index profile; changing one requires a new or rebuilt index.
36
+ Summaries describe a function's responsibility and role in its codebase, using its repository name, path, source, and surrounding file context. Generation uses the OpenAI Responses API with **`gpt-5.6-sol`** by default and requires `OPENAI_API_KEY`, including when Jina supplies the embeddings.
37
+
38
+ `use-summaries` stores each summary and its embedding in SQLite and enables a persistent repository-index setting. Subsequent indexing operations automatically generate summaries for new and changed files, including changes to surrounding context and file paths. Unchanged inputs reuse cached summaries and vectors; deleted functions disappear from summary search. Running `use-summaries` again is a no-op when summaries are already complete. Summary generation and embeddings are saved atomically, so a failed request does not leave partially updated callables.
39
+
40
+ `search-summary` uses a separate summary embedding store with the configured embedding provider and supports the same query, limit, threshold, and output options as `search`. JSON results include the summary; `--format summary` prints it alongside each match. `status` reports `summariesEnabled`, `summaryCount`, and `summaryProfile`.
42
41
 
43
- ## CLI
42
+ To choose a different summary model, run `slopdex use-summaries --summary-model <model-id>` or set `summaryModel` in `.slopdex/config.json`. The chosen model is persisted for future updates. Existing indexes migrate automatically; summaries remain disabled until enabled explicitly.
44
43
 
45
- When a command needs an index and none exists, Slopdex prints a notice to stderr and automatically indexes committed `HEAD`. Automatic initialization requires a clean Git worktree, just like `update-git`.
44
+ ### Combined Code And Purpose Analysis
46
45
 
47
- Index an exact committed snapshot and record its commit:
46
+ Once summaries are enabled and every indexed callable has a summary embedding, `cross-search` and `cohesion` automatically combine implementation and purpose similarity:
47
+
48
+ ```text
49
+ similarity = 0.5 * codeSimilarity + 0.5 * summarySimilarity
50
+ ```
48
51
 
49
52
  ```bash
50
- slopdex update-git --root /path/to/repository
53
+ slopdex use-summaries
54
+ slopdex cross-search --threshold 0.8 --format json
55
+ slopdex cohesion --threshold 0.8 --format summary
51
56
  ```
52
57
 
53
- Later Git updates read only files changed between the recorded commit and `HEAD`:
58
+ Both cosine scores and their average are calculated in one SQLite query. Thresholds (including half-open ranges), ranking, and neighbor/result limits apply to the combined score. Cohesion uses these combined neighbors for reciprocity, gap scores, file affinities, and groups.
59
+
60
+ For cross-repository search, both indexes must have complete, enabled summaries. If either index lacks them, the entire analysis uses code-only similarity. Cohesion likewise uses code-only scoring if its summary index is incomplete or disabled. The existing embedding profiles must match across repositories; summary-generator models may differ.
61
+
62
+ Combined JSON matches and cohesion pairs expose `codeSimilarity` and `summarySimilarity` alongside `similarity`. Cross-search rows include a `scoring` object with `similarityMode`, `similarityWeights`, and both summary-generator profiles. Cohesion records the mode and weights in `parameters` and the summary-generator profile in `repository.summaryProfile`. The mode is `"code-summary-average"` with weights `{ "code": 0.5, "summary": 0.5 }`, or `"code"` with weights `{ "code": 1, "summary": 0 }`. Text output labels combined scores.
63
+
64
+ Compare analysis results only when the similarity mode and weights match, as well as the embedding profile, summary-generator profiles, thresholds, and analysis scope. Enabling summaries can change rankings and cohesion metrics. `search` continues to use only code vectors, and `search-summary` uses only summary vectors.
65
+
66
+ ### Duplicate Analysis
67
+
68
+ Compare functions in different files and include functions that span at least four lines:
54
69
 
55
70
  ```bash
56
- slopdex update-git --root /path/to/repository --target HEAD
71
+ slopdex cross-search \
72
+ --cross-file-only \
73
+ --min-lines 4 \
74
+ --threshold 0.9 \
75
+ --limit 5
57
76
  ```
58
77
 
59
- Update or delete specific working-tree files without advancing the Git checkpoint:
78
+ The default output groups related functions into clusters:
60
79
 
61
- ```bash
62
- slopdex update-files src/service.ts src/model.ts
63
- slopdex delete-files src/removed.ts
80
+ ```text
81
+ Cluster 1 (3 functions, similarity 0.9124-0.9568)
82
+ src/auth/session.ts:18:1 :: validateSession
83
+ src/http/middleware.ts:42:1 :: authenticate
84
+ src/users/user-service.ts:27:3 :: UserService.authenticate
64
85
  ```
65
86
 
66
- Search by meaning:
87
+ A cluster contains functions connected by similarity matches. The similarity range covers the observed links in the cluster. Connected functions may be linked through another function, so review the source before deciding that code is duplicated.
88
+
89
+ ### Cohesion Analysis
90
+
91
+ Find related functions that are separated across files and directories:
67
92
 
68
93
  ```bash
69
- slopdex search "validate an authenticated session" --limit 10
94
+ slopdex cohesion \
95
+ --threshold 0.8 \
96
+ --neighbors 20 \
97
+ --limit 50 \
98
+ --format summary
99
+ ```
100
+
101
+ ```text
102
+ Cohesion: 184 functions analyzed, 37 semantic edges
103
+ same file 35.1% same folder 29.7% remote 35.2% mean distance 1.84
104
+
105
+ 1. gap 0.6053 similarity 0.9400 distance 4 reciprocal
106
+ src/auth/session.ts:18:1 :: validateSession
107
+ packages/http/middleware.ts:42:1 :: authenticate
70
108
  ```
71
109
 
72
- Find the nearest functions for each indexed function that has at least one match. Results are emitted as JSONL:
110
+ The summary divides semantic relationships into the same file, the same folder, and different folders. Mean distance increases when related functions are stored farther apart. The gap score combines semantic similarity with path distance and ranks pairs for review. `reciprocal` means both functions are among each other's nearest semantic matches.
111
+
112
+ Cohesion does not have a universal pass threshold. Compare results only when the similarity mode and weights, embedding and summary-generator profiles, threshold, neighbor count, and source scope are the same.
113
+
114
+ ## Limit Analysis Scope
115
+
116
+ Restrict source functions to a file or directory:
73
117
 
74
118
  ```bash
75
- slopdex cross-search --limit 5 > similarities.jsonl
119
+ slopdex cross-search --source-path src/services --format summary
120
+ slopdex cohesion --source-path src/services --format summary
76
121
  ```
77
122
 
78
- For a human-readable summary with each match indented beneath its source function:
123
+ Analyze functions added, changed, or moved since a commit:
79
124
 
80
125
  ```bash
81
- slopdex cross-search --limit 5 --format summary
126
+ slopdex cross-search --changed-since origin/main --format summary
127
+ slopdex cohesion --changed-since origin/main --format summary
82
128
  ```
83
129
 
84
- ```text
85
- src/users.ts :: Users.authenticate
86
- 0.9321 src/session.ts :: validateSession
87
- 0.8475 src/auth.ts :: authenticate
130
+ Analyze functions in files with uncommitted changes:
131
+
132
+ ```bash
133
+ slopdex cross-search --uncommitted --format summary
134
+ slopdex cohesion --uncommitted --format summary
88
135
  ```
89
136
 
90
- `--format summary` also produces compact file and function names for `search`. JSON remains the default format.
137
+ These options restrict the source functions. Slopdex still compares them with the full index.
91
138
 
92
- Use `--threshold` to omit weaker matches. The threshold is a raw cosine similarity and is applied before `--limit`:
139
+ Filter source symbols by qualified name with `-e <regex>` (or `--regexp <regex>`):
93
140
 
94
141
  ```bash
95
- slopdex cross-search --format summary --threshold 0.8 --limit 5
96
- slopdex search "validate session" --format summary --threshold 0.8
142
+ slopdex cross-search -e '^UserService\.' --format summary
143
+ slopdex cohesion -e 'validate|authenticate' --source-path src/auth
97
144
  ```
98
145
 
99
- Use an inclusive range to omit matches that are either weaker or stronger than the desired band:
146
+ The regex uses case-sensitive JavaScript syntax and matches qualified names such as `UserService.authenticate`. It filters only the source symbols for cross-search and cohesion; matching candidates are still drawn from the whole eligible index, including symbols that do not match the regex. It works with code-only and combined code/summary scoring.
147
+
148
+ Source restrictions combine by intersection:
100
149
 
101
150
  ```bash
102
- slopdex cross-search --format summary --threshold 0.85-0.95 --limit 5
151
+ slopdex cross-search -e 'validate' \
152
+ --source-path src \
153
+ --changed-since origin/main \
154
+ --uncommitted \
155
+ --min-lines 4 \
156
+ --cross-file-only
103
157
  ```
104
158
 
105
- `--min-similarity` remains available as an equivalent option; do not specify both.
106
- Source functions with no matches at the selected threshold are omitted.
107
- For same-index searches, each function pair is shown only in its first direction by default. Use
108
- `--include-symmetric-duplicates` to include both `A -> B` and `B -> A` results.
159
+ With both Git filters, a source must have changed since the specified commit and belong to an uncommitted file. Path, name, and minimum-line filters narrow the selection further. Existing target restrictions such as `--min-lines` and `--cross-file-only` continue to apply. The separate `--regex` option filters **both** analysis sources and matching candidates.
160
+
161
+ ## Output And Filters
109
162
 
110
- Restrict source functions to a file or every indexed file recursively under a directory. Matches are still selected from the whole target index:
163
+ `search` supports `json` and `summary` output. `cross-search` supports `json`, `summary`, and `clusters` output. Use `--format` to select one.
164
+
165
+ For `search` and `search-summary`, `-e` restricts result symbols before the result limit is applied:
111
166
 
112
167
  ```bash
113
- slopdex cross-search --source-path src/services --format summary
114
- slopdex cross-search --source-path src/service.ts --format summary
168
+ slopdex search "validate session" -e '^Session\.' --limit 10
169
+ slopdex search-summary "persist user data" -e 'save|persist' --limit 5
170
+ ```
171
+
172
+ Use `--threshold` with a minimum similarity or a range:
173
+
174
+ ```bash
175
+ slopdex search "validate session" --threshold 0.8 --format summary
176
+ slopdex cross-search --threshold 0.85-0.9 --format summary
115
177
  ```
116
178
 
117
- Restrict source functions to functions currently present but absent at a historical commit:
179
+ For a range, the lower bound is included and the upper bound is excluded. Similarity values depend on the embedding model, so use them to rank results from the same index profile.
180
+
181
+ Run `slopdex --help` for all commands and options.
182
+
183
+ ## Indexing And Data
184
+
185
+ ### Supported Languages
186
+
187
+ Languages are detected automatically from file extensions. A single index can contain multiple languages; search, purpose summaries, cross-search, and cohesion work across all of them.
188
+
189
+ | Language | Extensions | Extracted callables |
190
+ | --- | --- | --- |
191
+ | Python | `.py`, `.pyw` | Functions, async functions, class methods, constructors, generators, and bound lambdas; includes decorators in source |
192
+ | JavaScript | `.js`, `.mjs`, `.cjs` | Functions, generators, methods, constructors, and named function expressions/arrows |
193
+ | JSX | `.jsx` | JavaScript callables, including components returning JSX |
194
+ | TypeScript | `.ts`, `.mts`, `.cts` | Typed functions, methods, constructors, and named function expressions/arrows |
195
+ | TSX | `.tsx` | TypeScript callables, including generic components returning JSX |
196
+ | Rust | `.rs` | Functions, `impl` methods/associated functions, trait default methods, and `let`-bound closures |
197
+ | Go | `.go` | Functions, receiver methods, and function literals bound to variables or assignments |
198
+ | Java | `.java` | Methods, constructors (including compact record constructors), and variable-bound lambdas |
199
+ | C | `.c`, `.h` | Function definitions, including static/inline functions and functions returning pointers |
200
+
201
+ Qualified names include enclosing classes, functions, and explicit modules. Go methods include their receiver type (`Store[T].Get`); Rust trait implementations include the type and trait (`<Store<T> as Read>.read`). Definitions retain their source, signature, line/column locations, and stable identity for incremental updates. Declarations without bodies and anonymous callbacks are omitted. Extraction is syntactic: Rust macros and C macros are not expanded, and C preprocessor branches are indexed as written. `.h` files use the C grammar.
202
+
203
+ Tree-sitter grammars ship as package dependencies; no language server or project compiler configuration is required. Syntax errors produce warnings while recoverable callables are still indexed. Existing indexes pick up newly supported files during the next normal refresh.
204
+
205
+ Default exclusions cover dependency and build directories: `.git`, `.slopdex`, `node_modules`, `dist`, `build`, `coverage`, `vendor`, `generated`, `.venv`, `venv`, `__pycache__`, `.tox`, `.mypy_cache`, `.pytest_cache`, and `target`. Optional `include` and `exclude` glob arrays in `.slopdex/config.json` further restrict the indexed files.
206
+
207
+ Slopdex also respects root and nested `.gitignore` files using the [`ignore`](https://www.npmjs.com/package/ignore) package. Patterns, directory rules, anchoring, escapes, and `!` exceptions follow Git ignore semantics. A nested rule cannot re-include files beneath an excluded parent directory. These exclusions apply even to tracked files and when Git is unavailable; explicit `update-files` requests for ignored files are rejected.
208
+
209
+ Working-tree indexing and HEAD overlays use the current `.gitignore` files. Historical snapshots and committed-only Git indexing use the ignore files stored in the selected commit. Normal refreshes remove previously indexed files that become ignored and discover files that become eligible again, including when only ignore rules change. Configuration includes and negated ignore rules cannot override the built-in exclusions.
210
+
211
+ ### Index Updates
212
+
213
+ Before each analysis command, Slopdex updates its index from the current Git commit and the staged, unstaged, and untracked files in the working tree. The index is created at `.slopdex/index.sqlite` by default.
214
+
215
+ Add `.slopdex/` to the repository's `.gitignore` so the local index is not committed.
216
+
217
+ Slopdex sends extracted function source to the configured embedding provider. The function metadata and embeddings are stored in the local SQLite index.
218
+
219
+ Whole-repository cohesion analysis compares neighbors for every selected function. Use `--source-path`, `--changed-since`, or `--uncommitted` to reduce the scope in large repositories.
220
+
221
+ ### Inspecting Indexing Errors
222
+
223
+ Slopdex persists file and function indexing diagnostics in the SQLite `indexing_errors` table. Parse errors, parser exceptions, callable-extraction failures, file-read failures, and files exceeding `maxFileSize` retain references instead of silently disappearing. Healthy functions in partially parsed files and other healthy files remain searchable; malformed callables are omitted from semantic search and listed in the diagnostics.
118
224
 
119
225
  ```bash
120
- slopdex cross-search --added-since origin/main --limit 5
226
+ slopdex index-errors
227
+ slopdex index-errors --format summary
228
+ slopdex index-errors --index /path/to/another/index.sqlite
121
229
  ```
122
230
 
123
- Search one index against another. Both indexes must use exactly the same embedding profile:
231
+ This command reads the saved diagnostics without refreshing the index or calling an embedding/summary provider, so it works without API credentials. JSON includes the path, language, error code and message, file/function scope, recovered qualified name when available, line/column ranges, available source text, and Git/working-tree provenance. If Tree-sitter cannot identify a function, the unparsed region is still retained as a file diagnostic.
232
+
233
+ Every invocation warns on stderr while saved errors remain, including cached runs and cross-search target indexes. Suppress this warning with `--ignore-errors`:
124
234
 
125
235
  ```bash
126
- slopdex cross-search \
127
- --target-root /path/to/other-repository \
128
- --target-index /path/to/other-repository/.slopdex/index.sqlite
236
+ slopdex cross-search --ignore-errors
237
+ slopdex index-errors --format summary --ignore-errors
129
238
  ```
130
239
 
240
+ Silencing warnings does not delete diagnostics. `status` reports `indexingErrorCount` and `failedFileCount`; `functionCount` counts searchable callables. Normal updates retry failed files, including unchanged Git blobs. Errors clear when a file is successfully indexed, deleted, or excluded. Existing indexes migrate automatically and rescan once on their next full update to detect previously unreported parse failures. Diagnostics are committed atomically with the corresponding file update.
241
+
131
242
  ## Library
132
243
 
244
+ The package also exports the index and analysis APIs:
245
+
133
246
  ```ts
134
247
  import {
135
- JinaEmbeddingProvider,
136
- crossSearch,
248
+ OpenAIEmbeddingProvider,
137
249
  openCodeIndex,
138
250
  } from "@ninjaxtools/slopdex";
139
251
 
140
252
  const index = openCodeIndex({
141
253
  rootDir: "/path/to/repository",
142
- provider: new JinaEmbeddingProvider(),
254
+ provider: new OpenAIEmbeddingProvider(),
143
255
  });
144
256
 
145
257
  await index.updateFromGit();
@@ -149,33 +261,50 @@ const results = await index.similaritySearch({
149
261
  limit: 10,
150
262
  });
151
263
 
152
- for await (const result of crossSearch({
153
- source: index,
154
- sourceFilter: { type: "added-since", commit: "origin/main", path: "src/services" },
155
- limitPerFunction: 5,
156
- })) {
157
- console.log(result);
158
- }
159
-
160
264
  index.close();
161
265
  ```
162
266
 
163
- Standalone functions `updateFiles`, `updateFromGit`, `similaritySearch`, and `crossSearchFunctions` are also exported.
267
+ Exports also include `crossSearch`, `analyzeCohesion`, `JinaEmbeddingProvider`, and standalone functions for index updates and searches.
268
+
269
+ Library callers can set `sourceFilter.nameRegex` on cross-search/cohesion options for source-only filtering, together with `path` and the Git filter type. A `changed-since` filter also accepts `uncommitted: true`. Query searches accept `nameRegex` in `SimilaritySearchOptions` to filter result symbols. Cohesion reports record source restrictions in `parameters.sourceFilter` and label filtered analyses as `selected-sources`.
164
270
 
165
- ## Semantics
271
+ Use `index.indexErrors()` to inspect diagnostics through an open index, or the exported `readIndexErrors(indexPath)` to inspect a saved database without a provider. Error records use the exported `IndexingError` type.
166
272
 
167
- - Git updates read blobs from the target commit, not dirty working-tree contents.
168
- - Git updates abort when the worktree contains staged, unstaged, or untracked changes; commit or stash them first.
169
- - The Git checkpoint advances only after every changed file has parsed and embedded successfully.
170
- - Explicit updates mark files as working-tree sourced and do not move the checkpoint.
171
- - The next Git update after those changes are committed reconciles working-tree-sourced files to the commit.
172
- - `added-since X` means a current function whose logical callable identity was absent at X. It does not rely only on `firstSeenCommit`.
173
- - Search output is ordered by raw cosine similarity descending, then function ID ascending.
174
- - Threshold ranges are inclusive and are applied before the result limit.
175
- - Same-index cross-search excludes the source function itself and lists each unordered function pair once by default.
273
+ For summary search, call `await index.useSummaries()` after updating the index, then `await index.searchSummary({ query: "maintain the repository index" })`. Optionally pass `summaryProvider: new OpenAISummaryProvider({ model: "gpt-5.6-sol" })` when opening an index. Custom providers implement the exported `SummaryProvider` interface. The package also exports standalone `useSummaries` and `searchSummary` helpers.
176
274
 
177
275
  ## Development
178
276
 
179
277
  ```bash
180
278
  npm run check
181
279
  ```
280
+
281
+ To cross-check the shared-language fixtures against a built sibling checkout of `treesitter-index`, run:
282
+
283
+ ```bash
284
+ npm run check:parser-parity
285
+ # Or provide another reference executable:
286
+ npm run check:parser-parity -- /path/to/treesitter-index
287
+ ```
288
+
289
+ The default reference is `../treesitter-index/target/debug/treesitter-index`. The check covers eight shared languages; that project has no C grammar. Callable regression tests also run as part of `npm run check` without requiring the sibling checkout.
290
+
291
+ The projects index different information: `treesitter-index` includes declarations, types, imports, and `.pyi` stubs, while Slopdex indexes callable implementations (including nested callables and bound closures). Slopdex also supports `.pyw` and C. Its native Node grammar versions are pinned for compatibility with `tree-sitter@0.21`; the reference uses newer Python and Rust grammars. In particular, Rust `unsafe extern` blocks and async closures currently produce syntax-recovery warnings in Slopdex, so those cases are not covered by the passing parity fixtures.
292
+
293
+ After upgrading parser behavior, explicitly reparse previously indexed files with `slopdex update-files <path...>` to refresh their symbols, signatures, and embeddings even when the file contents are unchanged.
294
+
295
+ ## Configuration
296
+
297
+ Configuration is optional. Without `.slopdex/config.json`, Slopdex uses OpenAI's `text-embedding-3-large` model with 3072 dimensions and the built-in source exclusions.
298
+
299
+ Create `.slopdex/config.json` to change these settings:
300
+
301
+ ```json
302
+ {
303
+ "provider": "jina",
304
+ "model": "jina-embeddings-v4",
305
+ "dimensions": 1024,
306
+ "exclude": ["**/fixtures/**"]
307
+ }
308
+ ```
309
+
310
+ Use `OPENAI_API_KEY` for OpenAI and `JINA_API_KEY` for Jina AI. The provider, model, dimensions, and embedding strategy define the index profile. Rebuild the index with `--force-reindex` after changing the profile.