coderay 1.0.2__tar.gz → 1.0.4__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.
- {coderay-1.0.2/src/coderay.egg-info → coderay-1.0.4}/PKG-INFO +18 -22
- {coderay-1.0.2 → coderay-1.0.4}/README.md +2 -2
- {coderay-1.0.2 → coderay-1.0.4}/pyproject.toml +16 -19
- coderay-1.0.4/src/coderay/chunking/chunker.py +86 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/cli/commands.py +53 -65
- coderay-1.0.4/src/coderay/core/config.py +291 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/core/models.py +0 -1
- coderay-1.0.4/src/coderay/embedding/base.py +34 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/graph/__init__.py +6 -2
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/graph/builder.py +53 -24
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/graph/code_graph.py +6 -3
- coderay-1.0.4/src/coderay/graph/extractor.py +665 -0
- coderay-1.0.4/src/coderay/mcp_server/errors.py +11 -0
- coderay-1.0.4/src/coderay/mcp_server/server.py +223 -0
- coderay-1.0.4/src/coderay/parsing/base.py +139 -0
- coderay-1.0.4/src/coderay/parsing/languages.py +268 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/pipeline/indexer.py +38 -27
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/pipeline/watcher.py +9 -14
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/retrieval/boosting.py +11 -25
- coderay-1.0.4/src/coderay/retrieval/models.py +63 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/retrieval/search.py +10 -17
- coderay-1.0.4/src/coderay/skeleton/extractor.py +150 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/state/machine.py +5 -3
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/storage/lancedb.py +13 -19
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/vcs/git.py +2 -2
- {coderay-1.0.2 → coderay-1.0.4/src/coderay.egg-info}/PKG-INFO +18 -22
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay.egg-info/SOURCES.txt +4 -2
- coderay-1.0.4/src/coderay.egg-info/requires.txt +29 -0
- coderay-1.0.2/src/coderay/chunking/chunker.py +0 -127
- coderay-1.0.2/src/coderay/chunking/registry.py +0 -190
- coderay-1.0.2/src/coderay/core/config.py +0 -73
- coderay-1.0.2/src/coderay/embedding/base.py +0 -60
- coderay-1.0.2/src/coderay/embedding/openai.py +0 -87
- coderay-1.0.2/src/coderay/graph/extractor.py +0 -315
- coderay-1.0.2/src/coderay/mcp_server/server.py +0 -178
- coderay-1.0.2/src/coderay/skeleton/extractor.py +0 -140
- coderay-1.0.2/src/coderay.egg-info/requires.txt +0 -34
- {coderay-1.0.2 → coderay-1.0.4}/LICENSE +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/setup.cfg +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/chunking/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/cli/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/core/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/core/lock.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/core/timing.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/core/utils.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/embedding/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/embedding/local.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/mcp_server/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/pipeline/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/retrieval/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/skeleton/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/state/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/state/version.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/storage/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay/vcs/__init__.py +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay.egg-info/dependency_links.txt +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay.egg-info/entry_points.txt +0 -0
- {coderay-1.0.2 → coderay-1.0.4}/src/coderay.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coderay
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: X-ray your codebase — semantic search, code graphs, file skeletons, and MCP server
|
|
5
5
|
Author-email: Bogdan Copocean <bogdancopocean@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -20,35 +20,31 @@ Classifier: Topic :: Text Processing :: Indexing
|
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: python-dotenv>=1.
|
|
24
|
-
Requires-Dist: networkx>=3.
|
|
25
|
-
Requires-Dist: tree-sitter>=0.
|
|
23
|
+
Requires-Dist: python-dotenv>=1.2.0
|
|
24
|
+
Requires-Dist: networkx>=3.4
|
|
25
|
+
Requires-Dist: tree-sitter>=0.25.0
|
|
26
26
|
Requires-Dist: tree-sitter-python>=0.25.0
|
|
27
|
-
Requires-Dist: lancedb>=0.
|
|
28
|
-
Requires-Dist: pyyaml>=6.0
|
|
29
|
-
Requires-Dist: click>=8.0
|
|
30
|
-
Requires-Dist: filelock>=3.0
|
|
31
|
-
Requires-Dist: fastembed>=0.
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist: watchdog>=
|
|
34
|
-
Requires-Dist: pathspec>=0.
|
|
35
|
-
Provides-Extra: openai
|
|
36
|
-
Requires-Dist: openai>=1.0.0; extra == "openai"
|
|
27
|
+
Requires-Dist: lancedb>=0.29.0
|
|
28
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
29
|
+
Requires-Dist: click>=8.3.0
|
|
30
|
+
Requires-Dist: filelock>=3.25.0
|
|
31
|
+
Requires-Dist: fastembed>=0.7.0
|
|
32
|
+
Requires-Dist: fastmcp==3.1.0
|
|
33
|
+
Requires-Dist: watchdog>=6.0.0
|
|
34
|
+
Requires-Dist: pathspec>=1.0.0
|
|
37
35
|
Provides-Extra: languages
|
|
38
|
-
Requires-Dist: tree-sitter-javascript>=0.
|
|
39
|
-
Requires-Dist: tree-sitter-typescript>=0.23.
|
|
40
|
-
Requires-Dist: tree-sitter-go>=0.
|
|
36
|
+
Requires-Dist: tree-sitter-javascript>=0.25.0; extra == "languages"
|
|
37
|
+
Requires-Dist: tree-sitter-typescript>=0.23.2; extra == "languages"
|
|
38
|
+
Requires-Dist: tree-sitter-go>=0.25.0; extra == "languages"
|
|
41
39
|
Provides-Extra: dev
|
|
42
40
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
43
41
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
44
42
|
Requires-Dist: ruff>=0.8.0; extra == "dev"
|
|
45
43
|
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
46
|
-
Requires-Dist: openai>=1.0.0; extra == "dev"
|
|
47
|
-
Requires-Dist: httpx>=0.27.0; extra == "dev"
|
|
48
44
|
Provides-Extra: maintain
|
|
49
45
|
Requires-Dist: pylance>=0.15.0; extra == "maintain"
|
|
50
46
|
Provides-Extra: all
|
|
51
|
-
Requires-Dist: coderay[dev,languages,maintain
|
|
47
|
+
Requires-Dist: coderay[dev,languages,maintain]; extra == "all"
|
|
52
48
|
Dynamic: license-file
|
|
53
49
|
|
|
54
50
|
# CodeRay
|
|
@@ -72,7 +68,7 @@ AI coding assistants and a standalone CLI.
|
|
|
72
68
|
pip install coderay
|
|
73
69
|
```
|
|
74
70
|
|
|
75
|
-
With all extras (
|
|
71
|
+
With all extras (JS/TS/Go support, MCP server tools):
|
|
76
72
|
|
|
77
73
|
```bash
|
|
78
74
|
pip install "coderay[all]"
|
|
@@ -132,7 +128,7 @@ Optional `config.yaml` in the index directory:
|
|
|
132
128
|
|
|
133
129
|
```yaml
|
|
134
130
|
embedder:
|
|
135
|
-
provider: local
|
|
131
|
+
provider: local
|
|
136
132
|
model: all-MiniLM-L6-v2
|
|
137
133
|
dimensions: 384
|
|
138
134
|
|
|
@@ -19,7 +19,7 @@ AI coding assistants and a standalone CLI.
|
|
|
19
19
|
pip install coderay
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
With all extras (
|
|
22
|
+
With all extras (JS/TS/Go support, MCP server tools):
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
pip install "coderay[all]"
|
|
@@ -79,7 +79,7 @@ Optional `config.yaml` in the index directory:
|
|
|
79
79
|
|
|
80
80
|
```yaml
|
|
81
81
|
embedder:
|
|
82
|
-
provider: local
|
|
82
|
+
provider: local
|
|
83
83
|
model: all-MiniLM-L6-v2
|
|
84
84
|
dimensions: 384
|
|
85
85
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "coderay"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.4"
|
|
8
8
|
description = "X-ray your codebase — semantic search, code graphs, file skeletons, and MCP server"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -25,18 +25,18 @@ classifiers = [
|
|
|
25
25
|
"Topic :: Text Processing :: Indexing",
|
|
26
26
|
]
|
|
27
27
|
dependencies = [
|
|
28
|
-
"python-dotenv>=1.
|
|
29
|
-
"networkx>=3.
|
|
30
|
-
"tree-sitter>=0.
|
|
28
|
+
"python-dotenv>=1.2.0",
|
|
29
|
+
"networkx>=3.4",
|
|
30
|
+
"tree-sitter>=0.25.0",
|
|
31
31
|
"tree-sitter-python>=0.25.0",
|
|
32
|
-
"lancedb>=0.
|
|
33
|
-
"pyyaml>=6.0",
|
|
34
|
-
"click>=8.0",
|
|
35
|
-
"filelock>=3.0",
|
|
36
|
-
"fastembed>=0.
|
|
37
|
-
"
|
|
38
|
-
"watchdog>=
|
|
39
|
-
"pathspec>=0.
|
|
32
|
+
"lancedb>=0.29.0",
|
|
33
|
+
"pyyaml>=6.0.3",
|
|
34
|
+
"click>=8.3.0",
|
|
35
|
+
"filelock>=3.25.0",
|
|
36
|
+
"fastembed>=0.7.0",
|
|
37
|
+
"fastmcp==3.1.0",
|
|
38
|
+
"watchdog>=6.0.0",
|
|
39
|
+
"pathspec>=1.0.0",
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
[project.urls]
|
|
@@ -45,25 +45,22 @@ Repository = "https://github.com/bogdan-copocean/coderay"
|
|
|
45
45
|
Issues = "https://github.com/bogdan-copocean/coderay/issues"
|
|
46
46
|
|
|
47
47
|
[project.optional-dependencies]
|
|
48
|
-
openai = ["openai>=1.0.0"]
|
|
49
48
|
languages = [
|
|
50
|
-
"tree-sitter-javascript>=0.
|
|
51
|
-
"tree-sitter-typescript>=0.23.
|
|
52
|
-
"tree-sitter-go>=0.
|
|
49
|
+
"tree-sitter-javascript>=0.25.0",
|
|
50
|
+
"tree-sitter-typescript>=0.23.2",
|
|
51
|
+
"tree-sitter-go>=0.25.0",
|
|
53
52
|
]
|
|
54
53
|
dev = [
|
|
55
54
|
"pytest>=7.0",
|
|
56
55
|
"pytest-cov>=4.0",
|
|
57
56
|
"ruff>=0.8.0",
|
|
58
57
|
"mypy>=1.0.0",
|
|
59
|
-
"openai>=1.0.0",
|
|
60
|
-
"httpx>=0.27.0",
|
|
61
58
|
]
|
|
62
59
|
maintain = [
|
|
63
60
|
"pylance>=0.15.0",
|
|
64
61
|
]
|
|
65
62
|
all = [
|
|
66
|
-
"coderay[
|
|
63
|
+
"coderay[languages,dev,maintain]",
|
|
67
64
|
]
|
|
68
65
|
|
|
69
66
|
[project.scripts]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from coderay.core.models import Chunk
|
|
7
|
+
from coderay.parsing.base import BaseTreeSitterParser, parse_file
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
MODULE_SYMBOL = "<module>"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ChunkingTreeSitterParser(BaseTreeSitterParser):
|
|
15
|
+
"""Tree-sitter based chunker for source files."""
|
|
16
|
+
|
|
17
|
+
def collect_chunks(self) -> list[Chunk]:
|
|
18
|
+
"""Collect all chunks for the configured file and language."""
|
|
19
|
+
tree = self.get_tree()
|
|
20
|
+
root = tree.root_node
|
|
21
|
+
chunks: list[Chunk] = []
|
|
22
|
+
|
|
23
|
+
if preamble_lines := self._collect_preamble_lines(root):
|
|
24
|
+
chunks.append(
|
|
25
|
+
Chunk(
|
|
26
|
+
path=self.file_path,
|
|
27
|
+
start_line=1,
|
|
28
|
+
end_line=root.end_point[0] + 1,
|
|
29
|
+
symbol=MODULE_SYMBOL,
|
|
30
|
+
content="\n".join(preamble_lines),
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _dfs(node) -> None:
|
|
35
|
+
if node.type in self._ctx.lang_cfg.chunker.chunk_types:
|
|
36
|
+
if (
|
|
37
|
+
node.parent
|
|
38
|
+
and node.parent.type in self._ctx.lang_cfg.chunker.chunk_types
|
|
39
|
+
):
|
|
40
|
+
for child in node.children:
|
|
41
|
+
_dfs(child)
|
|
42
|
+
return
|
|
43
|
+
start_line = node.start_point[0] + 1
|
|
44
|
+
end_line = node.end_point[0] + 1
|
|
45
|
+
text = self.node_text(node)
|
|
46
|
+
symbol = self.identifier_from_node(node) or f"<{node.type}>"
|
|
47
|
+
chunks.append(
|
|
48
|
+
Chunk(
|
|
49
|
+
path=self.file_path,
|
|
50
|
+
start_line=start_line,
|
|
51
|
+
end_line=end_line,
|
|
52
|
+
symbol=symbol,
|
|
53
|
+
content=text,
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
for child in node.children:
|
|
57
|
+
_dfs(child)
|
|
58
|
+
|
|
59
|
+
_dfs(root)
|
|
60
|
+
logger.debug("Chunked %s: %d chunks", self.file_path, len(chunks))
|
|
61
|
+
return chunks
|
|
62
|
+
|
|
63
|
+
def _collect_preamble_lines(self, root) -> list[str]:
|
|
64
|
+
"""Collect top-level lines that are NOT part of any chunk_type definition."""
|
|
65
|
+
lines: list[str] = []
|
|
66
|
+
for child in root.children:
|
|
67
|
+
if child.type in self._ctx.lang_cfg.chunker.chunk_types:
|
|
68
|
+
continue
|
|
69
|
+
text = self.node_text(child).strip()
|
|
70
|
+
if text:
|
|
71
|
+
lines.append(text)
|
|
72
|
+
return lines
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def chunk_file(path: str | Path, content: str) -> list[Chunk]:
|
|
76
|
+
"""Chunk a source file into semantic units (functions, classes, preamble)."""
|
|
77
|
+
ctx = parse_file(path, content)
|
|
78
|
+
if ctx is None:
|
|
79
|
+
logger.warning("No language config for %s", path)
|
|
80
|
+
return []
|
|
81
|
+
parser = ChunkingTreeSitterParser(ctx)
|
|
82
|
+
try:
|
|
83
|
+
return parser.collect_chunks()
|
|
84
|
+
except Exception as e: # pragma: no cover - defensive logging
|
|
85
|
+
logger.warning("Chunking failed for %s: %s", path, e)
|
|
86
|
+
return []
|
|
@@ -14,7 +14,7 @@ from coderay.retrieval.search import Retrieval
|
|
|
14
14
|
from coderay.state.machine import StateMachine
|
|
15
15
|
from coderay.storage.lancedb import index_exists
|
|
16
16
|
|
|
17
|
-
# Load .env so
|
|
17
|
+
# Load .env so configuration and environment variables are available.
|
|
18
18
|
load_dotenv()
|
|
19
19
|
|
|
20
20
|
# ANSI colors (safe when not TTY we can strip or leave)
|
|
@@ -39,21 +39,22 @@ def _setup_logging(verbose: bool = False) -> None:
|
|
|
39
39
|
level=level,
|
|
40
40
|
datefmt="%H:%M:%S",
|
|
41
41
|
)
|
|
42
|
-
# Suppress noisy
|
|
43
|
-
for name in ("
|
|
42
|
+
# Suppress noisy HTTP logging; keep only warnings and errors
|
|
43
|
+
for name in ("httpx", "httpcore"):
|
|
44
44
|
logging.getLogger(name).setLevel(logging.WARNING)
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
@click.group()
|
|
48
|
-
@click.option("--index-dir", default=".index", help="Index directory (default .index)")
|
|
49
48
|
@click.option("-v", "--verbose", is_flag=True, default=False, help="Verbose logging")
|
|
50
49
|
@click.pass_context
|
|
51
|
-
def cli(ctx: click.Context,
|
|
50
|
+
def cli(ctx: click.Context, verbose: bool) -> None:
|
|
52
51
|
"""CodeRay — build, update, search, and inspect the index."""
|
|
53
52
|
_setup_logging(verbose)
|
|
54
53
|
ctx.ensure_object(dict)
|
|
55
|
-
ctx.obj["index_dir"] = Path(index_dir)
|
|
56
54
|
ctx.obj["verbose"] = verbose
|
|
55
|
+
from coderay.core.config import get_config
|
|
56
|
+
|
|
57
|
+
get_config()
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
@cli.command()
|
|
@@ -67,9 +68,12 @@ def cli(ctx: click.Context, index_dir: str, verbose: bool) -> None:
|
|
|
67
68
|
@click.pass_context
|
|
68
69
|
def build(ctx: click.Context, full: bool, repo: Path) -> None:
|
|
69
70
|
"""Build or rebuild the index."""
|
|
70
|
-
|
|
71
|
+
from coderay.core.config import get_config
|
|
72
|
+
|
|
73
|
+
config = get_config()
|
|
74
|
+
index_dir = Path(config.index.path)
|
|
71
75
|
index_dir.mkdir(parents=True, exist_ok=True)
|
|
72
|
-
indexer = Indexer(repo
|
|
76
|
+
indexer = Indexer(repo)
|
|
73
77
|
t0 = time.time()
|
|
74
78
|
try:
|
|
75
79
|
with acquire_indexer_lock(index_dir):
|
|
@@ -108,8 +112,11 @@ def build(ctx: click.Context, full: bool, repo: Path) -> None:
|
|
|
108
112
|
@click.pass_context
|
|
109
113
|
def update(ctx: click.Context, repo: Path) -> None:
|
|
110
114
|
"""Incremental update (only changed files). Uses file lock."""
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
from coderay.core.config import get_config
|
|
116
|
+
|
|
117
|
+
config = get_config()
|
|
118
|
+
index_dir = Path(config.index.path)
|
|
119
|
+
indexer = Indexer(repo)
|
|
113
120
|
t0 = time.time()
|
|
114
121
|
|
|
115
122
|
if not indexer.index_exists():
|
|
@@ -132,28 +139,29 @@ def update(ctx: click.Context, repo: Path) -> None:
|
|
|
132
139
|
@click.argument("query_text", required=True)
|
|
133
140
|
@click.option("--top-k", "top_k", default=10, help="Number of results")
|
|
134
141
|
@click.option("--path-prefix", help="Filter by path prefix")
|
|
135
|
-
@click.option("--language", help="Filter by language (e.g. python)")
|
|
136
142
|
@click.pass_context
|
|
137
143
|
def search_cmd(
|
|
138
144
|
ctx: click.Context,
|
|
139
145
|
query_text: str,
|
|
140
146
|
top_k: int,
|
|
141
147
|
path_prefix: str | None,
|
|
142
|
-
language: str | None,
|
|
143
148
|
) -> None:
|
|
144
149
|
"""Semantic search the index."""
|
|
145
|
-
|
|
150
|
+
from coderay.core.config import get_config
|
|
151
|
+
|
|
152
|
+
config = get_config()
|
|
153
|
+
index_dir = Path(config.index.path)
|
|
146
154
|
if not index_exists(index_dir):
|
|
147
155
|
click.echo(_color("No index found. Run 'coderay build' first.", YELLOW))
|
|
148
156
|
ctx.exit(1)
|
|
149
157
|
|
|
150
|
-
sm = StateMachine(
|
|
158
|
+
sm = StateMachine()
|
|
151
159
|
current_state = sm.current_state
|
|
152
160
|
if current_state is None:
|
|
153
161
|
click.echo(_color("No index state. Run 'coderay build' first.", YELLOW))
|
|
154
162
|
ctx.exit(1)
|
|
155
163
|
|
|
156
|
-
retrieval = Retrieval(
|
|
164
|
+
retrieval = Retrieval()
|
|
157
165
|
click.echo(_color(f"Searching: {query_text!r}", CYAN))
|
|
158
166
|
t0 = time.perf_counter()
|
|
159
167
|
|
|
@@ -162,7 +170,6 @@ def search_cmd(
|
|
|
162
170
|
current_state=current_state,
|
|
163
171
|
top_k=top_k,
|
|
164
172
|
path_prefix=path_prefix,
|
|
165
|
-
language=language,
|
|
166
173
|
)
|
|
167
174
|
elapsed = time.perf_counter() - t0
|
|
168
175
|
click.echo(_color(f"Query took {elapsed:.2f}s", BOLD))
|
|
@@ -171,31 +178,16 @@ def search_cmd(
|
|
|
171
178
|
click.echo(_color("No results.", YELLOW))
|
|
172
179
|
return
|
|
173
180
|
|
|
174
|
-
score_type = results[0].get("score_type", "cosine")
|
|
175
|
-
if score_type == "rrf":
|
|
176
|
-
click.echo(
|
|
177
|
-
_color("Scoring: hybrid (RRF) — relative ranking, not a percentage", CYAN)
|
|
178
|
-
)
|
|
179
|
-
else:
|
|
180
|
-
click.echo(_color("Scoring: cosine similarity (0-1)", CYAN))
|
|
181
|
-
|
|
182
181
|
for i, r in enumerate(results, 1):
|
|
183
182
|
path = r.get("path", "?")
|
|
184
183
|
start = r.get("start_line", 0)
|
|
185
184
|
end = r.get("end_line", 0)
|
|
186
185
|
symbol = r.get("symbol", "?")
|
|
187
|
-
score = r.get("score", 0)
|
|
188
|
-
if score_type == "cosine":
|
|
189
|
-
score_str = f"score={score:.4f} ({score:.0%})"
|
|
190
|
-
else:
|
|
191
|
-
score_str = f"score={score:.4f} (rrf)"
|
|
192
186
|
preview = (r.get("content") or "")[:200].replace("\n", " ")
|
|
193
187
|
if len(r.get("content") or "") > 200:
|
|
194
188
|
preview += "..."
|
|
195
189
|
click.echo("")
|
|
196
|
-
click.echo(
|
|
197
|
-
_color(f" {i}. {path}:{start}-{end} ({symbol}) {score_str}", GREEN)
|
|
198
|
-
)
|
|
190
|
+
click.echo(_color(f" {i}. {path}:{start}-{end} ({symbol})", GREEN))
|
|
199
191
|
click.echo(f" {preview}")
|
|
200
192
|
|
|
201
193
|
|
|
@@ -226,8 +218,11 @@ def list_cmd(
|
|
|
226
218
|
show_content: bool,
|
|
227
219
|
) -> None:
|
|
228
220
|
"""Show what is in the index: chunk counts and/or chunk list."""
|
|
229
|
-
|
|
230
|
-
|
|
221
|
+
from coderay.core.config import get_config
|
|
222
|
+
|
|
223
|
+
config = get_config()
|
|
224
|
+
index_dir = Path(config.index.path)
|
|
225
|
+
retrieval = Retrieval()
|
|
231
226
|
if not index_exists(index_dir):
|
|
232
227
|
click.echo(_color("No index found. Run 'coderay build' first.", YELLOW))
|
|
233
228
|
ctx.exit(1)
|
|
@@ -261,23 +256,23 @@ def list_cmd(
|
|
|
261
256
|
@click.pass_context
|
|
262
257
|
def status(ctx: click.Context) -> None:
|
|
263
258
|
"""Show index status: state, branch, commit, chunk count."""
|
|
264
|
-
|
|
259
|
+
from coderay.core.config import get_config
|
|
260
|
+
from coderay.state.version import read_index_version
|
|
261
|
+
from coderay.storage.lancedb import Store
|
|
262
|
+
|
|
263
|
+
config = get_config()
|
|
264
|
+
index_dir = Path(config.index.path)
|
|
265
265
|
if not index_exists(index_dir):
|
|
266
266
|
click.echo(_color("No index found. Run 'coderay build' first.", YELLOW))
|
|
267
267
|
ctx.exit(1)
|
|
268
268
|
|
|
269
|
-
sm = StateMachine(
|
|
269
|
+
sm = StateMachine()
|
|
270
270
|
state = sm.current_state
|
|
271
271
|
if state is None:
|
|
272
272
|
click.echo(_color("No index state found.", YELLOW))
|
|
273
273
|
ctx.exit(1)
|
|
274
274
|
|
|
275
|
-
|
|
276
|
-
from coderay.state.version import read_index_version
|
|
277
|
-
from coderay.storage.lancedb import Store
|
|
278
|
-
|
|
279
|
-
config = load_config(index_dir)
|
|
280
|
-
store = Store(index_dir, dimensions=get_embedding_dimensions(config))
|
|
275
|
+
store = Store()
|
|
281
276
|
chunks = store.chunk_count()
|
|
282
277
|
version = read_index_version(index_dir)
|
|
283
278
|
|
|
@@ -302,12 +297,15 @@ def status(ctx: click.Context) -> None:
|
|
|
302
297
|
@click.pass_context
|
|
303
298
|
def maintain(ctx: click.Context, repo: Path) -> None:
|
|
304
299
|
"""Reclaim space and compact the index."""
|
|
305
|
-
|
|
300
|
+
from coderay.core.config import get_config
|
|
301
|
+
|
|
302
|
+
config = get_config()
|
|
303
|
+
index_dir = Path(config.index.path)
|
|
306
304
|
if not index_exists(index_dir):
|
|
307
305
|
click.echo(_color("No index found. Run 'coderay build' first.", YELLOW))
|
|
308
306
|
ctx.exit(1)
|
|
309
307
|
click.echo(_color("Maintaining index...", CYAN))
|
|
310
|
-
indexer = Indexer(repo
|
|
308
|
+
indexer = Indexer(repo)
|
|
311
309
|
with acquire_indexer_lock(index_dir):
|
|
312
310
|
result = indexer.maintain()
|
|
313
311
|
if result.get("cleanup_done"):
|
|
@@ -357,8 +355,11 @@ def graph_cmd(
|
|
|
357
355
|
limit: int,
|
|
358
356
|
) -> None:
|
|
359
357
|
"""List call and import graph edges (who calls who, who imports what)."""
|
|
360
|
-
|
|
361
|
-
|
|
358
|
+
from coderay.core.config import get_config
|
|
359
|
+
|
|
360
|
+
config = get_config()
|
|
361
|
+
index_dir = Path(config.index.path)
|
|
362
|
+
retrieval = Retrieval()
|
|
362
363
|
if not index_exists(index_dir):
|
|
363
364
|
click.echo(_color("No index found. Run 'coderay build' first.", YELLOW))
|
|
364
365
|
ctx.exit(1)
|
|
@@ -394,12 +395,6 @@ def graph_cmd(
|
|
|
394
395
|
type=click.Path(exists=True, path_type=Path),
|
|
395
396
|
help="Repo root",
|
|
396
397
|
)
|
|
397
|
-
@click.option(
|
|
398
|
-
"--debounce",
|
|
399
|
-
type=float,
|
|
400
|
-
default=None,
|
|
401
|
-
help="Debounce seconds (default from config, typically 2s)",
|
|
402
|
-
)
|
|
403
398
|
@click.option(
|
|
404
399
|
"--quiet",
|
|
405
400
|
is_flag=True,
|
|
@@ -409,14 +404,14 @@ def graph_cmd(
|
|
|
409
404
|
def watch(
|
|
410
405
|
ctx: click.Context,
|
|
411
406
|
repo: Path,
|
|
412
|
-
debounce: float | None,
|
|
413
407
|
quiet: bool,
|
|
414
408
|
) -> None:
|
|
415
409
|
"""Watch for file changes and re-index automatically."""
|
|
416
|
-
from coderay.core.config import
|
|
410
|
+
from coderay.core.config import get_config
|
|
417
411
|
from coderay.pipeline.watcher import FileWatcher
|
|
418
412
|
|
|
419
|
-
|
|
413
|
+
config = get_config()
|
|
414
|
+
index_dir = Path(config.index.path)
|
|
420
415
|
if not index_exists(index_dir):
|
|
421
416
|
click.echo(
|
|
422
417
|
_color(
|
|
@@ -426,27 +421,20 @@ def watch(
|
|
|
426
421
|
)
|
|
427
422
|
ctx.exit(1)
|
|
428
423
|
|
|
429
|
-
config = load_config(index_dir)
|
|
430
|
-
if debounce is not None:
|
|
431
|
-
config.setdefault("watch", {})["debounce_seconds"] = debounce
|
|
432
|
-
|
|
433
424
|
if quiet:
|
|
434
425
|
logging.getLogger("coderay.pipeline.watcher").setLevel(logging.WARNING)
|
|
435
426
|
|
|
436
|
-
watcher = FileWatcher(repo, index_dir
|
|
427
|
+
watcher = FileWatcher(repo, index_dir)
|
|
437
428
|
|
|
438
429
|
click.echo(
|
|
439
430
|
_color(
|
|
440
431
|
f"Watching {repo.resolve()} "
|
|
441
|
-
f"(debounce={config.
|
|
442
|
-
f"Ctrl+C to stop)",
|
|
432
|
+
f"(debounce={config.watcher.debounce}s, Ctrl+C to stop)",
|
|
443
433
|
CYAN,
|
|
444
434
|
)
|
|
445
435
|
)
|
|
446
|
-
# Do an incremental update at start-up
|
|
447
|
-
index_dir = ctx.obj["index_dir"]
|
|
448
436
|
index_dir.mkdir(parents=True, exist_ok=True)
|
|
449
|
-
indexer = Indexer(repo
|
|
437
|
+
indexer = Indexer(repo)
|
|
450
438
|
indexer.update_incremental()
|
|
451
439
|
|
|
452
440
|
watcher.start()
|