vortexa 0.1.3__tar.gz → 0.2.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 (31) hide show
  1. {vortexa-0.1.3 → vortexa-0.2.0}/PKG-INFO +5 -7
  2. {vortexa-0.1.3 → vortexa-0.2.0}/pyproject.toml +3 -8
  3. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/core/chunking.py +1 -1
  4. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/core/embedding.py +2 -2
  5. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/core/indexer.py +15 -2
  6. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/interfaces/cli.py +46 -7
  7. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/interfaces/mcp_server.py +1 -1
  8. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/storage/walker.py +0 -1
  9. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa.egg-info/PKG-INFO +5 -7
  10. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa.egg-info/requires.txt +1 -5
  11. {vortexa-0.1.3 → vortexa-0.2.0}/LICENSE +0 -0
  12. {vortexa-0.1.3 → vortexa-0.2.0}/README.md +0 -0
  13. {vortexa-0.1.3 → vortexa-0.2.0}/setup.cfg +0 -0
  14. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/__init__.py +0 -0
  15. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/core/__init__.py +0 -0
  16. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/core/language.py +0 -0
  17. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/core/lf4_model.py +0 -0
  18. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/core/types.py +0 -0
  19. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/interfaces/__init__.py +0 -0
  20. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/interfaces/watcher.py +0 -0
  21. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/search/__init__.py +0 -0
  22. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/search/ranking.py +0 -0
  23. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/search/search.py +0 -0
  24. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/search/tokens.py +0 -0
  25. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/storage/__init__.py +0 -0
  26. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/storage/bm25.py +0 -0
  27. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa/storage/vector_store.py +0 -0
  28. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa.egg-info/SOURCES.txt +0 -0
  29. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa.egg-info/dependency_links.txt +0 -0
  30. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa.egg-info/entry_points.txt +0 -0
  31. {vortexa-0.1.3 → vortexa-0.2.0}/src/vortexa.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortexa
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: Codebase indexing and semantic search engine
5
5
  Author-email: VortexAI <koulabhay25@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -28,12 +28,10 @@ Requires-Dist: pathspec>=0.12.0
28
28
  Requires-Dist: huggingface-hub>=0.20.0
29
29
  Requires-Dist: tokenizers>=0.19.0
30
30
  Requires-Dist: safetensors>=0.4.0
31
- Provides-Extra: full
32
- Requires-Dist: model2vec>=0.3.0; extra == "full"
33
- Requires-Dist: sentence-transformers>=2.2.0; extra == "full"
34
- Requires-Dist: tree-sitter-language-pack>=0.1.0; extra == "full"
35
- Provides-Extra: mcp
36
- Requires-Dist: fastmcp>=2.0.0; extra == "mcp"
31
+ Requires-Dist: fastmcp>=2.0.0
32
+ Requires-Dist: model2vec>=0.3.0
33
+ Requires-Dist: sentence-transformers>=2.2.0
34
+ Requires-Dist: tree-sitter-language-pack>=0.1.0
37
35
  Dynamic: license-file
38
36
 
39
37
  <div align="center">
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vortexa"
7
- version = "0.1.3"
7
+ version = "0.2.0"
8
8
  description = "Codebase indexing and semantic search engine"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -34,17 +34,12 @@ dependencies = [
34
34
  "huggingface-hub>=0.20.0",
35
35
  "tokenizers>=0.19.0",
36
36
  "safetensors>=0.4.0",
37
- ]
38
-
39
- [project.optional-dependencies]
40
- full = [
37
+ "fastmcp>=2.0.0",
41
38
  "model2vec>=0.3.0",
42
39
  "sentence-transformers>=2.2.0",
43
40
  "tree-sitter-language-pack>=0.1.0",
44
41
  ]
45
- mcp = [
46
- "fastmcp>=2.0.0",
47
- ]
42
+
48
43
 
49
44
  [project.scripts]
50
45
  vortexa = "vortexa.interfaces.cli:main"
@@ -31,7 +31,7 @@ class ChunkBoundary:
31
31
  def _get_parser(language: str):
32
32
  """Get a tree-sitter parser for the given language. Returns None if unavailable."""
33
33
  try:
34
- from tree_sitter_language_pack import get_parser as _get_ts_parser # ty: ignore[unresolved-import]
34
+ from tree_sitter_language_pack import get_parser as _get_ts_parser
35
35
 
36
36
  return _get_ts_parser(language)
37
37
  except Exception:
@@ -61,7 +61,7 @@ class Model2VecEmbedder:
61
61
  if self._model is None:
62
62
  with self._lock:
63
63
  if self._model is None: # Double-checked locking
64
- from model2vec import StaticModel # ty: ignore[unresolved-import]
64
+ from model2vec import StaticModel
65
65
  logger.info("Loading embedding model: %s", self._model_id)
66
66
  self._model = StaticModel.from_pretrained(self._model_id)
67
67
 
@@ -111,7 +111,7 @@ class SentenceTransformerEmbedder:
111
111
  if self._model is None:
112
112
  with self._lock:
113
113
  if self._model is None:
114
- from sentence_transformers import SentenceTransformer # ty: ignore[unresolved-import]
114
+ from sentence_transformers import SentenceTransformer
115
115
  logger.info("Loading sentence-transformers model: %s", self._model_name)
116
116
  self._model = SentenceTransformer(self._model_name, device=self._device)
117
117
 
@@ -42,6 +42,19 @@ _MAX_FILE_BYTES = 1_000_000 # 1 MB
42
42
  # Hash of the chunking logic for memoization — bump when chunking changes
43
43
  _CHUNKING_LOGIC_VERSION = "2"
44
44
 
45
+ _GLOBAL_INDEX_ROOT = Path.home() / ".vortexa"
46
+
47
+
48
+ def _index_dir_for_root(root: Path) -> Path:
49
+ """Derive the global index directory for a given project root.
50
+
51
+ Stores under ``~/.vortexa/<hex_hash>/`` where *hex_hash* is the first
52
+ 16 chars of the SHA-256 of the resolved absolute path. This keeps every
53
+ indexed project isolated while avoiding path-length issues.
54
+ """
55
+ path_hash = hashlib.sha256(str(root).encode()).hexdigest()[:16]
56
+ return _GLOBAL_INDEX_ROOT / path_hash
57
+
45
58
 
46
59
  def _file_hash(path: Path) -> str:
47
60
  """Compute SHA256 hash of file contents."""
@@ -80,11 +93,11 @@ class CodebaseIndexer:
80
93
  :param root: Root directory of the codebase to index.
81
94
  :param model: Embedding model (Encoder or Embedder). Created from model_id if None.
82
95
  :param model_id: Model ID for auto-creating embedder (ignored if model provided).
83
- :param index_dir: Directory for persistent storage. Defaults to .jarvis/index/.
96
+ :param index_dir: Directory for persistent storage. Defaults to ~/.vortexa/<hash>/.
84
97
  :param chunk_config: Chunking configuration.
85
98
  """
86
99
  self.root = Path(root).resolve()
87
- self.index_dir = Path(index_dir) if index_dir else self.root / ".jarvis" / "index"
100
+ self.index_dir = Path(index_dir) if index_dir else _index_dir_for_root(self.root)
88
101
  self.chunk_config = chunk_config or ChunkConfig()
89
102
 
90
103
  # Resolve model: prefer explicit model, else create LF4Embedder
@@ -8,7 +8,7 @@ import logging
8
8
  import sys
9
9
  from pathlib import Path
10
10
 
11
- from vortexa.core.indexer import CodebaseIndexer
11
+ from vortexa.core.indexer import CodebaseIndexer, _index_dir_for_root
12
12
 
13
13
  logger = logging.getLogger(__name__)
14
14
 
@@ -112,7 +112,7 @@ def _build_parser() -> argparse.ArgumentParser:
112
112
  parser.add_argument(
113
113
  "environment_details",
114
114
  nargs="?",
115
- help="optional root path, JSON environment details, or Kilo environment details",
115
+ help="optional root path, JSON environment details, or text containing a root path to index and search (defaults to current directory)",
116
116
  )
117
117
  parser.add_argument(
118
118
  "--root",
@@ -138,12 +138,12 @@ def _build_parser() -> argparse.ArgumentParser:
138
138
  parser.add_argument(
139
139
  "--force",
140
140
  action="store_true",
141
- help="force a full re-index before searching",
141
+ help="force a full re-index before searching (ignores existing index)",
142
142
  )
143
143
  parser.add_argument(
144
144
  "--no-index",
145
145
  action="store_true",
146
- help="skip indexing and search the existing index only",
146
+ help="skip indexing entirely and search the existing index (errors if none)",
147
147
  )
148
148
  parser.add_argument(
149
149
  "--plain",
@@ -153,6 +153,12 @@ def _build_parser() -> argparse.ArgumentParser:
153
153
  return parser
154
154
 
155
155
 
156
+ def _has_existing_index(root: Path) -> bool:
157
+ """Check if a persistent index already exists for this root."""
158
+ index_dir = _index_dir_for_root(root)
159
+ return (index_dir / "state.lmdb").exists()
160
+
161
+
156
162
  def run_query(args: argparse.Namespace, parser: argparse.ArgumentParser) -> int:
157
163
  if not args.query:
158
164
  raise SystemExit("query is required when using -q/--query")
@@ -167,15 +173,48 @@ def run_query(args: argparse.Namespace, parser: argparse.ArgumentParser) -> int:
167
173
  parser.error(str(exc))
168
174
  indexer = CodebaseIndexer(root=root)
169
175
 
176
+ has_index = _has_existing_index(root)
177
+
170
178
  if args.no_index:
179
+ if not has_index:
180
+ raise SystemExit(
181
+ f"No index found for {root}. Run without --no-index to build one."
182
+ )
171
183
  indexer._load_state()
172
- else:
173
- stats = indexer.index(force=args.force, include_text_files=args.include_text)
184
+ print(f"[vortexa] Loaded existing index ({len(indexer.chunks)} chunks)", file=sys.stderr)
185
+ elif args.force:
186
+ stats = indexer.index(force=True, include_text_files=args.include_text)
174
187
  print(
175
- f"[vortexa] Ready: {stats.indexed_files} files, "
188
+ f"[vortexa] Re-indexed: {stats.indexed_files} files, "
176
189
  f"{stats.total_chunks} chunks in {stats.index_time_ms:.0f}ms",
177
190
  file=sys.stderr,
178
191
  )
192
+ else:
193
+ if not has_index:
194
+ stats = indexer.index(include_text_files=args.include_text)
195
+ print(
196
+ f"[vortexa] Indexed: {stats.indexed_files} files, "
197
+ f"{stats.total_chunks} chunks in {stats.index_time_ms:.0f}ms",
198
+ file=sys.stderr,
199
+ )
200
+ else:
201
+ indexer._load_state()
202
+ before = len(indexer.chunks)
203
+ stats = indexer.index(include_text_files=args.include_text)
204
+ after = len(indexer.chunks)
205
+ if after == before and stats.memo_misses == 0:
206
+ print(
207
+ f"[vortexa] Index up to date ({after} chunks, no changes)",
208
+ file=sys.stderr,
209
+ )
210
+ else:
211
+ added = after - before
212
+ label = f"+{added} chunks" if added else f"{stats.memo_misses} updated"
213
+ print(
214
+ f"[vortexa] Updated: {stats.indexed_files} files, "
215
+ f"{after} chunks ({label}) in {stats.index_time_ms:.0f}ms",
216
+ file=sys.stderr,
217
+ )
179
218
 
180
219
  query = args.query
181
220
  results = indexer.search(query, top_k=args.top_k, alpha=args.alpha)
@@ -14,7 +14,7 @@ from pathlib import Path
14
14
 
15
15
  os.environ.setdefault("HF_HUB_DISABLE_SYMLINKS_WARNING", "1")
16
16
 
17
- from fastmcp import FastMCP # ty: ignore[unresolved-import]
17
+ from fastmcp import FastMCP # ty: ignore[unresolved-import, unused-ignore-comment]
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
@@ -34,7 +34,6 @@ _DEFAULT_IGNORED_DIRS: frozenset[str] = frozenset(
34
34
  "dist/",
35
35
  "build/",
36
36
  ".eggs/",
37
- ".jarvis/",
38
37
  }
39
38
  )
40
39
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortexa
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: Codebase indexing and semantic search engine
5
5
  Author-email: VortexAI <koulabhay25@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -28,12 +28,10 @@ Requires-Dist: pathspec>=0.12.0
28
28
  Requires-Dist: huggingface-hub>=0.20.0
29
29
  Requires-Dist: tokenizers>=0.19.0
30
30
  Requires-Dist: safetensors>=0.4.0
31
- Provides-Extra: full
32
- Requires-Dist: model2vec>=0.3.0; extra == "full"
33
- Requires-Dist: sentence-transformers>=2.2.0; extra == "full"
34
- Requires-Dist: tree-sitter-language-pack>=0.1.0; extra == "full"
35
- Provides-Extra: mcp
36
- Requires-Dist: fastmcp>=2.0.0; extra == "mcp"
31
+ Requires-Dist: fastmcp>=2.0.0
32
+ Requires-Dist: model2vec>=0.3.0
33
+ Requires-Dist: sentence-transformers>=2.2.0
34
+ Requires-Dist: tree-sitter-language-pack>=0.1.0
37
35
  Dynamic: license-file
38
36
 
39
37
  <div align="center">
@@ -5,11 +5,7 @@ pathspec>=0.12.0
5
5
  huggingface-hub>=0.20.0
6
6
  tokenizers>=0.19.0
7
7
  safetensors>=0.4.0
8
-
9
- [full]
8
+ fastmcp>=2.0.0
10
9
  model2vec>=0.3.0
11
10
  sentence-transformers>=2.2.0
12
11
  tree-sitter-language-pack>=0.1.0
13
-
14
- [mcp]
15
- fastmcp>=2.0.0
File without changes
File without changes
File without changes
File without changes