code-memory 1.0.10__tar.gz → 1.0.11__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.
- {code_memory-1.0.10 → code_memory-1.0.11}/.github/workflows/release-binaries.yml +1 -1
- {code_memory-1.0.10 → code_memory-1.0.11}/PKG-INFO +1 -1
- {code_memory-1.0.10 → code_memory-1.0.11}/parser.py +87 -28
- {code_memory-1.0.10 → code_memory-1.0.11}/pyproject.toml +1 -1
- {code_memory-1.0.10 → code_memory-1.0.11}/.github/workflows/ci.yml +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/.github/workflows/publish.yml +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/.gitignore +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/.python-version +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/CHANGELOG.md +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/CONTRIBUTING.md +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/LICENSE +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/Makefile +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/README.md +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/code-memory.spec +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/db.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/doc_parser.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/errors.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/git_search.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/hooks/hook-sentence_transformers.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/hooks/hook-sqlite_vec.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/hooks/hook-tree_sitter.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/hooks/hook-tree_sitter_languages.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/logging_config.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/prompts/milestone_1.xml +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/prompts/milestone_2.xml +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/prompts/milestone_3.xml +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/prompts/milestone_4.xml +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/prompts/milestone_5.xml +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/prompts/milestone_6.xml +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/queries.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/server.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/tests/__init__.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/tests/conftest.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/tests/test_errors.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/tests/test_logging.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/tests/test_tools.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/tests/test_validation.py +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/uv.lock +0 -0
- {code_memory-1.0.10 → code_memory-1.0.11}/validation.py +0 -0
|
@@ -53,7 +53,7 @@ jobs:
|
|
|
53
53
|
|
|
54
54
|
- name: Download embedding model (cache for offline use)
|
|
55
55
|
run: |
|
|
56
|
-
python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('jinaai/jina-code-embeddings-
|
|
56
|
+
python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('jinaai/jina-code-embeddings-0.5b', trust_remote_code=True)"
|
|
57
57
|
|
|
58
58
|
- name: Build binary with PyInstaller (Linux/Windows)
|
|
59
59
|
if: matrix.os != 'macos-latest' || matrix.target == 'macos-x86_64'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-memory
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.11
|
|
4
4
|
Summary: A deterministic, high-precision code intelligence MCP server
|
|
5
5
|
Project-URL: Homepage, https://github.com/kapillamba4/code-memory
|
|
6
6
|
Project-URL: Documentation, https://github.com/kapillamba4/code-memory#readme
|
|
@@ -47,22 +47,79 @@ def _load_gitignore_spec(root_dir: str) -> pathspec.PathSpec | None:
|
|
|
47
47
|
return None
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
class GitignoreMatcher:
|
|
51
|
+
"""Manages .gitignore matching with support for nested .gitignore files.
|
|
52
|
+
|
|
53
|
+
Git reads all .gitignore files in the directory tree, not just the root.
|
|
54
|
+
Each nested .gitignore applies patterns relative to its own directory.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, root_dir: str):
|
|
58
|
+
self.root_dir = root_dir
|
|
59
|
+
self._specs: dict[str, pathspec.PathSpec] = {}
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
# Load root .gitignore if it exists
|
|
62
|
+
root_spec = _load_gitignore_spec(root_dir)
|
|
63
|
+
if root_spec:
|
|
64
|
+
self._specs["."] = root_spec
|
|
65
|
+
|
|
66
|
+
def _load_spec_for_dir(self, abs_dir: str, rel_dir: str) -> None:
|
|
67
|
+
"""Load .gitignore for a directory if not already loaded."""
|
|
68
|
+
if rel_dir in self._specs:
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
spec = _load_gitignore_spec(abs_dir)
|
|
72
|
+
if spec:
|
|
73
|
+
self._specs[rel_dir] = spec
|
|
74
|
+
|
|
75
|
+
def _get_parent_specs(self, rel_path: str) -> list[tuple[str, pathspec.PathSpec]]:
|
|
76
|
+
"""Get all applicable gitignore specs for a given path.
|
|
77
|
+
|
|
78
|
+
Returns list of (base_dir, spec) tuples for specs that apply to this path.
|
|
79
|
+
"""
|
|
80
|
+
result = []
|
|
81
|
+
path_parts = rel_path.replace("\\", "/").split("/")
|
|
82
|
+
|
|
83
|
+
# Check all ancestor directories that have .gitignore files
|
|
84
|
+
for base_dir, spec in self._specs.items():
|
|
85
|
+
if base_dir == ".":
|
|
86
|
+
# Root spec applies to everything
|
|
87
|
+
result.append((base_dir, spec))
|
|
88
|
+
else:
|
|
89
|
+
# Nested spec only applies if path is under that directory
|
|
90
|
+
base_parts = base_dir.replace("\\", "/").split("/")
|
|
91
|
+
if path_parts[:len(base_parts)] == base_parts:
|
|
92
|
+
result.append((base_dir, spec))
|
|
93
|
+
|
|
94
|
+
return result
|
|
95
|
+
|
|
96
|
+
def should_skip(self, rel_path: str, is_dir: bool) -> bool:
|
|
97
|
+
"""Check if a path should be skipped based on all applicable .gitignore patterns."""
|
|
98
|
+
# Normalize path separators for matching
|
|
99
|
+
rel_path = rel_path.replace("\\", "/")
|
|
100
|
+
|
|
101
|
+
for base_dir, spec in self._get_parent_specs(rel_path):
|
|
102
|
+
# For nested gitignores, compute path relative to that gitignore's directory
|
|
103
|
+
if base_dir == ".":
|
|
104
|
+
check_path = rel_path
|
|
105
|
+
else:
|
|
106
|
+
base_prefix = base_dir.replace("\\", "/") + "/"
|
|
107
|
+
if rel_path.startswith(base_prefix):
|
|
108
|
+
check_path = rel_path[len(base_prefix):]
|
|
109
|
+
else:
|
|
110
|
+
continue
|
|
64
111
|
|
|
65
|
-
|
|
112
|
+
# Check both the path as-is and with trailing slash for directories
|
|
113
|
+
if spec.match_file(check_path):
|
|
114
|
+
return True
|
|
115
|
+
if is_dir and spec.match_file(check_path + "/"):
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
def check_dir_for_gitignore(self, abs_dir: str, rel_dir: str) -> None:
|
|
121
|
+
"""Check if directory contains a .gitignore and load it."""
|
|
122
|
+
self._load_spec_for_dir(abs_dir, rel_dir)
|
|
66
123
|
|
|
67
124
|
# ── File extensions we consider "source code" ─────────────────────────
|
|
68
125
|
_SOURCE_EXTENSIONS = frozenset({
|
|
@@ -397,8 +454,9 @@ def index_file(filepath: str, db) -> dict:
|
|
|
397
454
|
def index_directory(dirpath: str, db) -> list[dict]:
|
|
398
455
|
"""Recursively index all source files under *dirpath*.
|
|
399
456
|
|
|
400
|
-
Skips directories in ``_SKIP_DIRS``, files matching ``.gitignore`` patterns
|
|
401
|
-
and unchanged files. Indexes any file
|
|
457
|
+
Skips directories in ``_SKIP_DIRS``, files matching ``.gitignore`` patterns
|
|
458
|
+
(including nested .gitignore files), and unchanged files. Indexes any file
|
|
459
|
+
with a recognised source-code extension.
|
|
402
460
|
|
|
403
461
|
Args:
|
|
404
462
|
dirpath: Root directory to scan.
|
|
@@ -413,32 +471,33 @@ def index_directory(dirpath: str, db) -> list[dict]:
|
|
|
413
471
|
dirpath = os.path.abspath(dirpath)
|
|
414
472
|
total_start = time.perf_counter()
|
|
415
473
|
|
|
416
|
-
#
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
logger.debug("Loaded .gitignore patterns from %s", dirpath)
|
|
474
|
+
# Initialize gitignore matcher (supports nested .gitignore files)
|
|
475
|
+
gitignore = GitignoreMatcher(dirpath)
|
|
476
|
+
logger.debug("Initialized gitignore matcher for %s", dirpath)
|
|
420
477
|
|
|
421
478
|
for root, dirs, files in os.walk(dirpath, topdown=True):
|
|
422
479
|
rel_root = os.path.relpath(root, dirpath)
|
|
423
480
|
|
|
481
|
+
# Check for .gitignore in current directory and load it
|
|
482
|
+
if rel_root != ".":
|
|
483
|
+
gitignore.check_dir_for_gitignore(root, rel_root)
|
|
484
|
+
|
|
424
485
|
# Prune skipped directories in-place (always-skip + gitignore)
|
|
425
486
|
def _should_keep_dir(d: str) -> bool:
|
|
426
487
|
if d in _SKIP_DIRS or d.endswith(".egg-info"):
|
|
427
488
|
return False
|
|
428
|
-
if
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
return False
|
|
489
|
+
rel_path = os.path.join(rel_root, d) if rel_root != "." else d
|
|
490
|
+
if gitignore.should_skip(rel_path, is_dir=True):
|
|
491
|
+
return False
|
|
432
492
|
return True
|
|
433
493
|
|
|
434
494
|
dirs[:] = [d for d in dirs if _should_keep_dir(d)]
|
|
435
495
|
|
|
436
496
|
for fname in sorted(files):
|
|
437
497
|
# Skip files matching .gitignore patterns
|
|
438
|
-
if
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
continue
|
|
498
|
+
rel_path = os.path.join(rel_root, fname) if rel_root != "." else fname
|
|
499
|
+
if gitignore.should_skip(rel_path, is_dir=False):
|
|
500
|
+
continue
|
|
442
501
|
|
|
443
502
|
ext = os.path.splitext(fname)[1].lower()
|
|
444
503
|
# Accept files with known extensions, or files with a
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|