cocoindex-code 0.2.40__tar.gz → 0.2.42__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 (26) hide show
  1. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/PKG-INFO +43 -4
  2. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/README.md +41 -2
  3. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/_version.py +2 -2
  4. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/client.py +18 -8
  5. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/daemon.py +3 -2
  6. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/query.py +46 -13
  7. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/settings.py +2 -0
  8. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/.gitignore +0 -0
  9. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/LICENSE +0 -0
  10. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/pyproject.toml +0 -0
  11. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/__init__.py +0 -0
  12. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/__main__.py +0 -0
  13. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/_daemon_paths.py +0 -0
  14. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/chunking.py +0 -0
  15. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/cli.py +0 -0
  16. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/embedder_defaults.py +0 -0
  17. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/embedder_params.py +0 -0
  18. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/file_walk.py +0 -0
  19. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/grep.py +0 -0
  20. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/indexer.py +0 -0
  21. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/litellm_embedder.py +0 -0
  22. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/project.py +0 -0
  23. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/protocol.py +0 -0
  24. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/schema.py +0 -0
  25. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/server.py +0 -0
  26. {cocoindex_code-0.2.40 → cocoindex_code-0.2.42}/src/cocoindex_code/shared.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: cocoindex-code
3
- Version: 0.2.40
3
+ Version: 0.2.42
4
4
  Summary: MCP server for indexing and querying codebases using CocoIndex
5
5
  Project-URL: Homepage, https://github.com/cocoindex-io/cocoindex-code
6
6
  Project-URL: Repository, https://github.com/cocoindex-io/cocoindex-code
@@ -141,7 +141,7 @@ For [Grok](https://github.com/xai-org/grok) users, install via Grok's plugin sys
141
141
  | Component | Purpose |
142
142
  |-----------|---------|
143
143
  | **Skill** (`skills/ccc/`) | Agent runs `ccc search` / `ccc index` via the CLI (same as Claude Code above) |
144
- | **Hook** (`hooks/hooks.json`) | `SessionStart` → incremental `ccc index` when `.cocoindex_code/` exists |
144
+ | **Hook** (`hooks/hooks.json`) | `SessionStart` + `PostToolUse` (Edit/Write/…) → incremental `ccc index` when `.cocoindex_code/` exists |
145
145
  | **MCP** (`.mcp.json`) | `ccc mcp` stdio server — `search` tool with `refresh_index=true` by default |
146
146
 
147
147
  Grok does **not** import Claude's `enabledPlugins` or plugin cache; install separately even if you already use cocoindex in Claude Code.
@@ -162,7 +162,7 @@ Prefer the GitHub shorthand (`cocoindex-io/cocoindex-code`) for install — `gro
162
162
 
163
163
  Install and enable as above, then disable the optional components:
164
164
 
165
- 1. **Hooks** — open `/hooks`, select the `SessionStart` hook from `cocoindex-code`, press `Space` to disable.
165
+ 1. **Hooks** — open `/hooks`, select the `SessionStart` / `PostToolUse` hooks from `cocoindex-code`, press `Space` to disable.
166
166
  2. **MCP** — open `/mcps`, select `cocoindex-code`, press `Space` to disable; or persist in `~/.grok/config.toml`:
167
167
 
168
168
  ```toml
@@ -182,6 +182,27 @@ mcps = false
182
182
  mcps = false
183
183
  ```
184
184
 
185
+ #### Oh My Pi plugin
186
+
187
+ [Oh My Pi](https://github.com/can1357/oh-my-pi) reads `.omp-plugin/marketplace.json` (preferred) and falls back to `.claude-plugin/marketplace.json`.
188
+
189
+ ```bash
190
+ omp plugin marketplace add cocoindex-io/cocoindex-code
191
+ omp plugin install cocoindex-code@cocoindex-code --scope project
192
+ ```
193
+
194
+ Then restart the session (`/reload-plugins` does not reload extensions). Requires `ccc` on `PATH` (`uv tool install --upgrade 'cocoindex-code[full]'`).
195
+
196
+ | Component | Purpose |
197
+ |-----------|---------|
198
+ | **Skill** (`skills/ccc/`) | Agent runs `ccc search` / `ccc index` via the CLI |
199
+ | **Hook** (`extensions/ccc-index.ts` via `package.json#omp.extensions`) | `session_start` + post-edit `tool_result` → incremental `ccc index` when `.cocoindex_code/` exists |
200
+ | **MCP** (`.mcp.json`) | `ccc mcp` stdio server |
201
+
202
+ OMP does **not** execute Claude `hooks/hooks.json` command hooks. The TypeScript extension is the OMP equivalent of the Claude/Grok SessionStart + PostToolUse pair.
203
+
204
+ Skill-only (no MCP, no auto-index): install the skill via `npx skills add cocoindex-io/cocoindex-code`, or run `ccc search` / `ccc index` from the shell.
205
+
185
206
  ### MCP Server
186
207
 
187
208
  Alternatively, use `ccc mcp` to run as an MCP server:
@@ -246,6 +267,23 @@ Add a local MCP server in `~/.config/kilo/kilo.jsonc`, `kilo.jsonc`, or `.kilo/k
246
267
  ```
247
268
  </details>
248
269
 
270
+ <details>
271
+ <summary>Oh My Pi</summary>
272
+
273
+ Prefer the marketplace install above. Manual project MCP (`.omp/mcp.json`):
274
+
275
+ ```json
276
+ {
277
+ "mcpServers": {
278
+ "cocoindex-code": {
279
+ "command": "ccc",
280
+ "args": ["mcp"]
281
+ }
282
+ }
283
+ }
284
+ ```
285
+ </details>
286
+
249
287
  Once configured, the agent automatically decides when semantic code search is helpful — finding code by description, exploring unfamiliar codebases, fuzzy/conceptual matches, or locating implementations without knowing exact names.
250
288
 
251
289
  > **Note:** The `cocoindex-code` command (without subcommand) still works as an MCP server for backward compatibility. It auto-creates settings from environment variables on first run.
@@ -850,6 +888,7 @@ embedding:
850
888
  | css | | `.css`, `.scss` |
851
889
  | dart | | `.dart` |
852
890
  | dtd | | `.dtd` |
891
+ | elixir | | `.ex`, `.exs` |
853
892
  | fortran | f, f90, f95, f03 | `.f`, `.f90`, `.f95`, `.f03` |
854
893
  | go | golang | `.go` |
855
894
  | html | | `.html`, `.htm` |
@@ -97,7 +97,7 @@ For [Grok](https://github.com/xai-org/grok) users, install via Grok's plugin sys
97
97
  | Component | Purpose |
98
98
  |-----------|---------|
99
99
  | **Skill** (`skills/ccc/`) | Agent runs `ccc search` / `ccc index` via the CLI (same as Claude Code above) |
100
- | **Hook** (`hooks/hooks.json`) | `SessionStart` → incremental `ccc index` when `.cocoindex_code/` exists |
100
+ | **Hook** (`hooks/hooks.json`) | `SessionStart` + `PostToolUse` (Edit/Write/…) → incremental `ccc index` when `.cocoindex_code/` exists |
101
101
  | **MCP** (`.mcp.json`) | `ccc mcp` stdio server — `search` tool with `refresh_index=true` by default |
102
102
 
103
103
  Grok does **not** import Claude's `enabledPlugins` or plugin cache; install separately even if you already use cocoindex in Claude Code.
@@ -118,7 +118,7 @@ Prefer the GitHub shorthand (`cocoindex-io/cocoindex-code`) for install — `gro
118
118
 
119
119
  Install and enable as above, then disable the optional components:
120
120
 
121
- 1. **Hooks** — open `/hooks`, select the `SessionStart` hook from `cocoindex-code`, press `Space` to disable.
121
+ 1. **Hooks** — open `/hooks`, select the `SessionStart` / `PostToolUse` hooks from `cocoindex-code`, press `Space` to disable.
122
122
  2. **MCP** — open `/mcps`, select `cocoindex-code`, press `Space` to disable; or persist in `~/.grok/config.toml`:
123
123
 
124
124
  ```toml
@@ -138,6 +138,27 @@ mcps = false
138
138
  mcps = false
139
139
  ```
140
140
 
141
+ #### Oh My Pi plugin
142
+
143
+ [Oh My Pi](https://github.com/can1357/oh-my-pi) reads `.omp-plugin/marketplace.json` (preferred) and falls back to `.claude-plugin/marketplace.json`.
144
+
145
+ ```bash
146
+ omp plugin marketplace add cocoindex-io/cocoindex-code
147
+ omp plugin install cocoindex-code@cocoindex-code --scope project
148
+ ```
149
+
150
+ Then restart the session (`/reload-plugins` does not reload extensions). Requires `ccc` on `PATH` (`uv tool install --upgrade 'cocoindex-code[full]'`).
151
+
152
+ | Component | Purpose |
153
+ |-----------|---------|
154
+ | **Skill** (`skills/ccc/`) | Agent runs `ccc search` / `ccc index` via the CLI |
155
+ | **Hook** (`extensions/ccc-index.ts` via `package.json#omp.extensions`) | `session_start` + post-edit `tool_result` → incremental `ccc index` when `.cocoindex_code/` exists |
156
+ | **MCP** (`.mcp.json`) | `ccc mcp` stdio server |
157
+
158
+ OMP does **not** execute Claude `hooks/hooks.json` command hooks. The TypeScript extension is the OMP equivalent of the Claude/Grok SessionStart + PostToolUse pair.
159
+
160
+ Skill-only (no MCP, no auto-index): install the skill via `npx skills add cocoindex-io/cocoindex-code`, or run `ccc search` / `ccc index` from the shell.
161
+
141
162
  ### MCP Server
142
163
 
143
164
  Alternatively, use `ccc mcp` to run as an MCP server:
@@ -202,6 +223,23 @@ Add a local MCP server in `~/.config/kilo/kilo.jsonc`, `kilo.jsonc`, or `.kilo/k
202
223
  ```
203
224
  </details>
204
225
 
226
+ <details>
227
+ <summary>Oh My Pi</summary>
228
+
229
+ Prefer the marketplace install above. Manual project MCP (`.omp/mcp.json`):
230
+
231
+ ```json
232
+ {
233
+ "mcpServers": {
234
+ "cocoindex-code": {
235
+ "command": "ccc",
236
+ "args": ["mcp"]
237
+ }
238
+ }
239
+ }
240
+ ```
241
+ </details>
242
+
205
243
  Once configured, the agent automatically decides when semantic code search is helpful — finding code by description, exploring unfamiliar codebases, fuzzy/conceptual matches, or locating implementations without knowing exact names.
206
244
 
207
245
  > **Note:** The `cocoindex-code` command (without subcommand) still works as an MCP server for backward compatibility. It auto-creates settings from environment variables on first run.
@@ -806,6 +844,7 @@ embedding:
806
844
  | css | | `.css`, `.scss` |
807
845
  | dart | | `.dart` |
808
846
  | dtd | | `.dtd` |
847
+ | elixir | | `.ex`, `.exs` |
809
848
  | fortran | f, f90, f95, f03 | `.f`, `.f90`, `.f95`, `.f03` |
810
849
  | go | golang | `.go` |
811
850
  | html | | `.html`, `.htm` |
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.40'
22
- __version_tuple__ = version_tuple = (0, 2, 40)
21
+ __version__ = version = '0.2.42'
22
+ __version_tuple__ = version_tuple = (0, 2, 42)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -228,6 +228,19 @@ def _handle_vanished_daemon() -> None:
228
228
  )
229
229
 
230
230
 
231
+ def _daemon_error(resp: ErrorResponse) -> RuntimeError:
232
+ """Build the exception for an ``ErrorResponse`` from the daemon.
233
+
234
+ The daemon-side traceback is appended when present: without it the caller
235
+ only ever sees the client's own re-raise frames, which says nothing about
236
+ where the failure actually happened (issue #270).
237
+ """
238
+ detail = f"Daemon error: {resp.message}"
239
+ if resp.traceback:
240
+ detail += f"\n{resp.traceback}"
241
+ return RuntimeError(detail)
242
+
243
+
231
244
  class _HandshakeResult(NamedTuple):
232
245
  conn: Connection
233
246
  resp: HandshakeResponse
@@ -261,7 +274,7 @@ def _raw_connect_and_handshake() -> _HandshakeResult:
261
274
  raise DaemonProtocolError(f"Undecodable handshake reply from daemon: {e}") from e
262
275
  if isinstance(resp, ErrorResponse):
263
276
  conn.close()
264
- raise RuntimeError(f"Daemon error: {resp.message}")
277
+ raise _daemon_error(resp)
265
278
  if not isinstance(resp, HandshakeResponse):
266
279
  conn.close()
267
280
  raise RuntimeError(f"Unexpected handshake response: {type(resp).__name__}")
@@ -340,7 +353,7 @@ def _send(req: Request) -> Response:
340
353
  conn.close()
341
354
  resp = decode_response(data)
342
355
  if isinstance(resp, ErrorResponse):
343
- raise RuntimeError(f"Daemon error: {resp.message}")
356
+ raise _daemon_error(resp)
344
357
  return resp
345
358
 
346
359
 
@@ -366,7 +379,7 @@ def index(
366
379
  raise RuntimeError("Connection to daemon lost during indexing")
367
380
  resp = decode_response(data)
368
381
  if isinstance(resp, ErrorResponse):
369
- raise RuntimeError(f"Daemon error: {resp.message}")
382
+ raise _daemon_error(resp)
370
383
  if isinstance(resp, IndexWaitingNotice):
371
384
  if on_waiting is not None:
372
385
  on_waiting()
@@ -419,7 +432,7 @@ def search(
419
432
  raise RuntimeError("Connection to daemon lost during search")
420
433
  resp = decode_response(data)
421
434
  if isinstance(resp, ErrorResponse):
422
- raise RuntimeError(f"Daemon error: {resp.message}")
435
+ raise _daemon_error(resp)
423
436
  if isinstance(resp, IndexWaitingNotice):
424
437
  if on_waiting is not None:
425
438
  on_waiting()
@@ -496,10 +509,7 @@ def doctor(
496
509
  raise RuntimeError("Connection to daemon lost during doctor checks")
497
510
  resp = decode_response(data)
498
511
  if isinstance(resp, ErrorResponse):
499
- detail = f"Daemon error: {resp.message}"
500
- if resp.traceback:
501
- detail += f"\n{resp.traceback}"
502
- raise RuntimeError(detail)
512
+ raise _daemon_error(resp)
503
513
  if isinstance(resp, DoctorResponse):
504
514
  results.append(resp.result)
505
515
  if on_result is not None:
@@ -346,7 +346,8 @@ async def _search_with_wait(
346
346
  offset=req.offset,
347
347
  )
348
348
  except Exception as e:
349
- yield ErrorResponse(message=str(e))
349
+ logger.exception("Error handling search request")
350
+ yield ErrorResponse(message=str(e), traceback=traceback.format_exc())
350
351
 
351
352
 
352
353
  async def _handle_doctor(
@@ -605,7 +606,7 @@ async def _dispatch(
605
606
  return ErrorResponse(message=f"Unknown request type: {type(req).__name__}")
606
607
  except Exception as e:
607
608
  logger.exception("Error dispatching request")
608
- return ErrorResponse(message=str(e))
609
+ return ErrorResponse(message=str(e), traceback=traceback.format_exc())
609
610
 
610
611
 
611
612
  # ---------------------------------------------------------------------------
@@ -16,6 +16,33 @@ def _l2_to_score(distance: float) -> float:
16
16
  return 1.0 - distance * distance / 2.0
17
17
 
18
18
 
19
+ def _checked(rows: list[tuple[Any, ...]], query_shape: str) -> list[tuple[Any, ...]]:
20
+ """Return *rows*, failing loudly if any of them carries a NULL distance.
21
+
22
+ ``code_chunks_vec`` is a vec0 virtual table whose ``distance`` is a *hidden*
23
+ column: sqlite-vec populates it only under the KNN query plan and yields
24
+ NULL for it on a plain full scan. A NULL therefore means the plan we asked
25
+ for is not the plan we got, which is a bug worth reporting rather than a
26
+ result worth ranking. Left unchecked, the NULL flows into ``_l2_to_score``
27
+ and surfaces as an unrelated-looking ``TypeError: unsupported operand
28
+ type(s) for *: 'NoneType' and 'NoneType'`` (issue #270).
29
+
30
+ Only KNN queries need this guard: ``_full_scan_query`` computes its own
31
+ ``vec_distance_L2(...)``, which works under any plan and raises (never
32
+ returns NULL) on bad input.
33
+ """
34
+ bad = next((row for row in rows if row[5] is None), None)
35
+ if bad is not None:
36
+ raise RuntimeError(
37
+ f"Vector index returned a row with no distance ({query_shape}, "
38
+ f"file_path={bad[0]!r}) — the sqlite-vec KNN query plan was not "
39
+ "used. Please report this at "
40
+ "https://github.com/cocoindex-io/cocoindex-code/issues along with "
41
+ "the output of `ccc doctor`."
42
+ )
43
+ return rows
44
+
45
+
19
46
  def _knn_query(
20
47
  conn: sqlite3.Connection,
21
48
  embedding_bytes: bytes,
@@ -24,24 +51,30 @@ def _knn_query(
24
51
  ) -> list[tuple[Any, ...]]:
25
52
  """Run a vec0 KNN query, optionally constrained to a language partition."""
26
53
  if language is not None:
27
- return conn.execute(
54
+ return _checked(
55
+ conn.execute(
56
+ """
57
+ SELECT file_path, language, content, start_line, end_line, distance
58
+ FROM code_chunks_vec
59
+ WHERE embedding MATCH ? AND k = ? AND language = ?
60
+ ORDER BY distance
61
+ """,
62
+ (embedding_bytes, k, language),
63
+ ).fetchall(),
64
+ f"knn language={language!r}",
65
+ )
66
+ return _checked(
67
+ conn.execute(
28
68
  """
29
69
  SELECT file_path, language, content, start_line, end_line, distance
30
70
  FROM code_chunks_vec
31
- WHERE embedding MATCH ? AND k = ? AND language = ?
71
+ WHERE embedding MATCH ? AND k = ?
32
72
  ORDER BY distance
33
73
  """,
34
- (embedding_bytes, k, language),
35
- ).fetchall()
36
- return conn.execute(
37
- """
38
- SELECT file_path, language, content, start_line, end_line, distance
39
- FROM code_chunks_vec
40
- WHERE embedding MATCH ? AND k = ?
41
- ORDER BY distance
42
- """,
43
- (embedding_bytes, k),
44
- ).fetchall()
74
+ (embedding_bytes, k),
75
+ ).fetchall(),
76
+ "knn unfiltered",
77
+ )
45
78
 
46
79
 
47
80
  def _full_scan_query(
@@ -38,6 +38,8 @@ DEFAULT_INCLUDED_PATTERNS: list[str] = [
38
38
  "**/*.hh", # C++ headers
39
39
  "**/*.cs", # C#
40
40
  "**/*.dart", # Dart
41
+ "**/*.ex", # Elixir
42
+ "**/*.exs", # Elixir scripts
41
43
  "**/*.sql", # SQL
42
44
  "**/*.sh", # Shell
43
45
  "**/*.bash", # Bash
File without changes