@embedder/embedder 1.1.1 → 2.0.6

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 (80) hide show
  1. package/assets/_commonjsHelpers-C1uRMj_j.js +1 -0
  2. package/assets/cli-Ccqv_iE9.js +865 -0
  3. package/assets/devtools-B4JKPJyU.js +1 -0
  4. package/assets/index-B3hJUcJE.js +1 -0
  5. package/assets/index-BSFXqd02.js +1 -0
  6. package/assets/index-DjlgDVzW.js +1 -0
  7. package/cli.js +2 -0
  8. package/package.json +15 -96
  9. package/LICENSE +0 -36
  10. package/bundle/embedder.js +0 -618
  11. package/bundle/gdb-debugger-python/gdb_bridge.py +0 -392
  12. package/bundle/gdb-debugger-python/requirements.txt +0 -1
  13. package/bundle/postinstall-for-users.js +0 -515
  14. package/bundle/repomap-bridge.js +0 -6
  15. package/bundle/repomap-python/importance.py +0 -58
  16. package/bundle/repomap-python/queries/tree-sitter-language-pack/README.md +0 -9
  17. package/bundle/repomap-python/queries/tree-sitter-language-pack/arduino-tags.scm +0 -5
  18. package/bundle/repomap-python/queries/tree-sitter-language-pack/c-tags.scm +0 -9
  19. package/bundle/repomap-python/queries/tree-sitter-language-pack/chatito-tags.scm +0 -16
  20. package/bundle/repomap-python/queries/tree-sitter-language-pack/commonlisp-tags.scm +0 -122
  21. package/bundle/repomap-python/queries/tree-sitter-language-pack/cpp-tags.scm +0 -15
  22. package/bundle/repomap-python/queries/tree-sitter-language-pack/csharp-tags.scm +0 -26
  23. package/bundle/repomap-python/queries/tree-sitter-language-pack/d-tags.scm +0 -26
  24. package/bundle/repomap-python/queries/tree-sitter-language-pack/dart-tags.scm +0 -92
  25. package/bundle/repomap-python/queries/tree-sitter-language-pack/elisp-tags.scm +0 -5
  26. package/bundle/repomap-python/queries/tree-sitter-language-pack/elixir-tags.scm +0 -54
  27. package/bundle/repomap-python/queries/tree-sitter-language-pack/elm-tags.scm +0 -19
  28. package/bundle/repomap-python/queries/tree-sitter-language-pack/gleam-tags.scm +0 -41
  29. package/bundle/repomap-python/queries/tree-sitter-language-pack/go-tags.scm +0 -42
  30. package/bundle/repomap-python/queries/tree-sitter-language-pack/java-tags.scm +0 -20
  31. package/bundle/repomap-python/queries/tree-sitter-language-pack/javascript-tags.scm +0 -88
  32. package/bundle/repomap-python/queries/tree-sitter-language-pack/lua-tags.scm +0 -34
  33. package/bundle/repomap-python/queries/tree-sitter-language-pack/ocaml-tags.scm +0 -115
  34. package/bundle/repomap-python/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +0 -98
  35. package/bundle/repomap-python/queries/tree-sitter-language-pack/pony-tags.scm +0 -39
  36. package/bundle/repomap-python/queries/tree-sitter-language-pack/properties-tags.scm +0 -5
  37. package/bundle/repomap-python/queries/tree-sitter-language-pack/python-tags.scm +0 -14
  38. package/bundle/repomap-python/queries/tree-sitter-language-pack/r-tags.scm +0 -21
  39. package/bundle/repomap-python/queries/tree-sitter-language-pack/racket-tags.scm +0 -12
  40. package/bundle/repomap-python/queries/tree-sitter-language-pack/ruby-tags.scm +0 -64
  41. package/bundle/repomap-python/queries/tree-sitter-language-pack/rust-tags.scm +0 -60
  42. package/bundle/repomap-python/queries/tree-sitter-language-pack/solidity-tags.scm +0 -43
  43. package/bundle/repomap-python/queries/tree-sitter-language-pack/swift-tags.scm +0 -51
  44. package/bundle/repomap-python/queries/tree-sitter-language-pack/udev-tags.scm +0 -20
  45. package/bundle/repomap-python/queries/tree-sitter-languages/README.md +0 -24
  46. package/bundle/repomap-python/queries/tree-sitter-languages/c-tags.scm +0 -9
  47. package/bundle/repomap-python/queries/tree-sitter-languages/c_sharp-tags.scm +0 -46
  48. package/bundle/repomap-python/queries/tree-sitter-languages/cpp-tags.scm +0 -15
  49. package/bundle/repomap-python/queries/tree-sitter-languages/dart-tags.scm +0 -91
  50. package/bundle/repomap-python/queries/tree-sitter-languages/elisp-tags.scm +0 -8
  51. package/bundle/repomap-python/queries/tree-sitter-languages/elixir-tags.scm +0 -54
  52. package/bundle/repomap-python/queries/tree-sitter-languages/elm-tags.scm +0 -19
  53. package/bundle/repomap-python/queries/tree-sitter-languages/go-tags.scm +0 -30
  54. package/bundle/repomap-python/queries/tree-sitter-languages/hcl-tags.scm +0 -77
  55. package/bundle/repomap-python/queries/tree-sitter-languages/java-tags.scm +0 -20
  56. package/bundle/repomap-python/queries/tree-sitter-languages/javascript-tags.scm +0 -88
  57. package/bundle/repomap-python/queries/tree-sitter-languages/kotlin-tags.scm +0 -27
  58. package/bundle/repomap-python/queries/tree-sitter-languages/ocaml-tags.scm +0 -115
  59. package/bundle/repomap-python/queries/tree-sitter-languages/ocaml_interface-tags.scm +0 -98
  60. package/bundle/repomap-python/queries/tree-sitter-languages/php-tags.scm +0 -26
  61. package/bundle/repomap-python/queries/tree-sitter-languages/python-tags.scm +0 -12
  62. package/bundle/repomap-python/queries/tree-sitter-languages/ql-tags.scm +0 -26
  63. package/bundle/repomap-python/queries/tree-sitter-languages/ruby-tags.scm +0 -64
  64. package/bundle/repomap-python/queries/tree-sitter-languages/rust-tags.scm +0 -60
  65. package/bundle/repomap-python/queries/tree-sitter-languages/scala-tags.scm +0 -65
  66. package/bundle/repomap-python/queries/tree-sitter-languages/typescript-tags.scm +0 -41
  67. package/bundle/repomap-python/repomap.py +0 -229
  68. package/bundle/repomap-python/repomap_bridge.py +0 -234
  69. package/bundle/repomap-python/repomap_class.py +0 -637
  70. package/bundle/repomap-python/repomap_server.py +0 -561
  71. package/bundle/repomap-python/requirements.txt +0 -7
  72. package/bundle/repomap-python/scm.py +0 -59
  73. package/bundle/repomap-python/utils.py +0 -58
  74. package/bundle/sandbox-macos-permissive-closed.sb +0 -26
  75. package/bundle/sandbox-macos-permissive-open.sb +0 -19
  76. package/bundle/sandbox-macos-permissive-proxied.sb +0 -31
  77. package/bundle/sandbox-macos-restrictive-closed.sb +0 -87
  78. package/bundle/sandbox-macos-restrictive-open.sb +0 -90
  79. package/bundle/sandbox-macos-restrictive-proxied.sb +0 -92
  80. package/postinstall.js +0 -42
@@ -1,637 +0,0 @@
1
- """
2
- RepoMap class for generating repository maps.
3
- """
4
-
5
- import os
6
- import sys
7
- from pathlib import Path
8
- from collections import namedtuple, defaultdict
9
- from typing import List, Dict, Set, Optional, Tuple, Callable, Any, Union
10
- import shutil
11
- import sqlite3
12
- from utils import Tag
13
- from dataclasses import dataclass
14
- import diskcache
15
- import networkx as nx
16
- import diskcache
17
- from grep_ast import TreeContext
18
- from utils import count_tokens, read_text, Tag
19
- from scm import get_scm_fname
20
- from importance import filter_important_files
21
-
22
-
23
- @dataclass
24
- class FileReport:
25
- excluded: Dict[str, str] # File -> exclusion reason with status
26
- definition_matches: int # Total definition tags
27
- reference_matches: int # Total reference tags
28
- total_files_considered: int # Total files provided as input
29
-
30
-
31
-
32
- # Constants
33
- CACHE_VERSION = 1
34
- import os
35
-
36
- TAGS_CACHE_DIR = os.path.join(os.getcwd(), f".repomap.tags.cache.v{CACHE_VERSION}")
37
- SQLITE_ERRORS = (sqlite3.OperationalError, sqlite3.DatabaseError)
38
-
39
- # Tag namedtuple for storing parsed code definitions and references
40
- Tag = namedtuple("Tag", "rel_fname fname line name kind".split())
41
-
42
-
43
- class RepoMap:
44
- """Main class for generating repository maps."""
45
-
46
- def __init__(
47
- self,
48
- map_tokens: int = 1024,
49
- root: str = None,
50
- token_counter_func: Callable[[str], int] = count_tokens,
51
- file_reader_func: Callable[[str], Optional[str]] = read_text,
52
- output_handler_funcs: Dict[str, Callable] = None,
53
- repo_content_prefix: Optional[str] = None,
54
- verbose: bool = False,
55
- max_context_window: Optional[int] = None,
56
- map_mul_no_files: int = 8,
57
- refresh: str = "auto",
58
- exclude_unranked: bool = False
59
- ):
60
- """Initialize RepoMap instance."""
61
- self.map_tokens = map_tokens
62
- self.max_map_tokens = map_tokens
63
- self.root = Path(root or os.getcwd()).resolve()
64
- self.token_count_func_internal = token_counter_func
65
- self.read_text_func_internal = file_reader_func
66
- self.repo_content_prefix = repo_content_prefix
67
- self.verbose = verbose
68
- self.max_context_window = max_context_window
69
- self.map_mul_no_files = map_mul_no_files
70
- self.refresh = refresh
71
- self.exclude_unranked = exclude_unranked
72
-
73
- # Set up output handlers
74
- if output_handler_funcs is None:
75
- output_handler_funcs = {
76
- 'info': print,
77
- 'warning': print,
78
- 'error': print
79
- }
80
- self.output_handlers = output_handler_funcs
81
-
82
- # Initialize caches
83
- self.tree_cache = {}
84
- self.tree_context_cache = {}
85
- self.map_cache = {}
86
-
87
- # Load persistent tags cache
88
- self.load_tags_cache()
89
-
90
- def load_tags_cache(self):
91
- """Load the persistent tags cache."""
92
- cache_dir = self.root / TAGS_CACHE_DIR
93
- try:
94
- self.TAGS_CACHE = diskcache.Cache(str(cache_dir))
95
- except Exception as e:
96
- self.output_handlers['warning'](f"Failed to load tags cache: {e}")
97
- self.TAGS_CACHE = {}
98
-
99
- def save_tags_cache(self):
100
- """Save the tags cache (no-op as diskcache handles persistence)."""
101
- pass
102
-
103
- def tags_cache_error(self):
104
- """Handle tags cache errors."""
105
- try:
106
- cache_dir = self.root / TAGS_CACHE_DIR
107
- if cache_dir.exists():
108
- shutil.rmtree(cache_dir)
109
- self.load_tags_cache()
110
- except Exception:
111
- self.output_handlers['warning']("Failed to recreate tags cache, using in-memory cache")
112
- self.TAGS_CACHE = {}
113
-
114
- def token_count(self, text: str) -> int:
115
- """Count tokens in text with sampling optimization for long texts."""
116
- if not text:
117
- return 0
118
-
119
- len_text = len(text)
120
- if len_text < 200:
121
- return self.token_count_func_internal(text)
122
-
123
- # Sample for longer texts
124
- lines = text.splitlines(keepends=True)
125
- num_lines = len(lines)
126
-
127
- step = max(1, num_lines // 100)
128
- sampled_lines = lines[::step]
129
- sample_text = "".join(sampled_lines)
130
-
131
- if not sample_text:
132
- return self.token_count_func_internal(text)
133
-
134
- sample_tokens = self.token_count_func_internal(sample_text)
135
-
136
- if len(sample_text) == 0:
137
- return self.token_count_func_internal(text)
138
-
139
- est_tokens = (sample_tokens / len(sample_text)) * len_text
140
- return int(est_tokens)
141
-
142
- def get_rel_fname(self, fname: str) -> str:
143
- """Get relative filename from absolute path."""
144
- try:
145
- return str(Path(fname).relative_to(self.root))
146
- except ValueError:
147
- return fname
148
-
149
- def get_mtime(self, fname: str) -> Optional[float]:
150
- """Get file modification time."""
151
- try:
152
- return os.path.getmtime(fname)
153
- except FileNotFoundError:
154
- self.output_handlers['warning'](f"File not found: {fname}")
155
- return None
156
-
157
- def get_tags(self, fname: str, rel_fname: str) -> List[Tag]:
158
- """Get tags for a file, using cache when possible."""
159
- file_mtime = self.get_mtime(fname)
160
- if file_mtime is None:
161
- return []
162
-
163
- try:
164
- # Handle both diskcache Cache and in-memory dict
165
- if isinstance(self.TAGS_CACHE, dict):
166
- cached_entry = self.TAGS_CACHE.get(fname)
167
- else:
168
- cached_entry = self.TAGS_CACHE.get(fname)
169
-
170
- if cached_entry and cached_entry.get("mtime") == file_mtime:
171
- return cached_entry["data"]
172
- except SQLITE_ERRORS:
173
- self.tags_cache_error()
174
-
175
- # Cache miss or file changed
176
- tags = self.get_tags_raw(fname, rel_fname)
177
-
178
- try:
179
- self.TAGS_CACHE[fname] = {"mtime": file_mtime, "data": tags}
180
- except SQLITE_ERRORS:
181
- self.tags_cache_error()
182
-
183
- return tags
184
-
185
- def get_tags_raw(self, fname: str, rel_fname: str) -> List[Tag]:
186
- """Parse file to extract tags using Tree-sitter."""
187
- try:
188
- import tree_sitter
189
- from grep_ast import filename_to_lang
190
- from grep_ast.tsl import get_language, get_parser
191
- except ImportError:
192
- print("Error: tree-sitter and grep-ast are required. Install with: pip install tree-sitter grep-ast")
193
- sys.exit(1)
194
-
195
- lang = filename_to_lang(fname)
196
- if not lang:
197
- return []
198
-
199
- try:
200
- language = get_language(lang)
201
- parser = get_parser(lang)
202
- except Exception as err:
203
- self.output_handlers['error'](f"Skipping file {fname}: {err}")
204
- return []
205
-
206
- scm_fname = get_scm_fname(lang)
207
- if not scm_fname:
208
- return []
209
-
210
- code = self.read_text_func_internal(fname)
211
- if not code:
212
- return []
213
-
214
- try:
215
- tree = parser.parse(bytes(code, "utf-8"))
216
-
217
- # Load query from SCM file
218
- query_text = read_text(scm_fname, silent=True)
219
- if not query_text:
220
- return []
221
-
222
- query = tree_sitter.Query(language, query_text)
223
- cursor = tree_sitter.QueryCursor(query)
224
- matches = cursor.matches(tree.root_node)
225
-
226
- tags = []
227
- # Process matches as (pattern_index, captures_dict) tuples
228
- for pattern_index, captures_dict in matches:
229
- for capture_name, nodes in captures_dict.items():
230
- if "name.definition" in capture_name:
231
- kind = "def"
232
- elif "name.reference" in capture_name:
233
- kind = "ref"
234
- else:
235
- # Skip other capture types like 'reference.call' if not needed for tagging
236
- continue
237
-
238
- # Process all nodes for this capture
239
- for node in nodes:
240
- line_num = node.start_point[0] + 1
241
- # Handle potential None value
242
- name = node.text.decode('utf-8') if node.text else ""
243
-
244
- tags.append(Tag(
245
- rel_fname=rel_fname,
246
- fname=fname,
247
- line=line_num,
248
- name=name,
249
- kind=kind
250
- ))
251
-
252
- return tags
253
-
254
- except Exception as e:
255
- self.output_handlers['error'](f"Error parsing {fname}: {e}")
256
- return []
257
-
258
- def get_ranked_tags(
259
- self,
260
- chat_fnames: List[str],
261
- other_fnames: List[str],
262
- mentioned_fnames: Optional[Set[str]] = None,
263
- mentioned_idents: Optional[Set[str]] = None
264
- ) -> Tuple[List[Tuple[float, Tag]], FileReport]:
265
- """Get ranked tags using PageRank algorithm with file report."""
266
- # Return empty list and empty report if no files
267
- if not chat_fnames and not other_fnames:
268
- return [], FileReport([], {}, 0, 0, 0)
269
-
270
- # Initialize file report early
271
- included: List[str] = []
272
- excluded: Dict[str, str] = {}
273
- total_definitions = 0
274
- total_references = 0
275
- if mentioned_fnames is None:
276
- mentioned_fnames = set()
277
- if mentioned_idents is None:
278
- mentioned_idents = set()
279
-
280
- # Normalize paths to absolute
281
- def normalize_path(path):
282
- return str(Path(path).resolve())
283
-
284
- chat_fnames = [normalize_path(f) for f in chat_fnames]
285
- other_fnames = [normalize_path(f) for f in other_fnames]
286
-
287
- # Initialize file report
288
- included: List[str] = []
289
- excluded: Dict[str, str] = {}
290
- input_files: Dict[str, Dict] = {}
291
- total_definitions = 0
292
- total_references = 0
293
-
294
- # Collect all tags
295
- defines = defaultdict(set)
296
- references = defaultdict(set)
297
- definitions = defaultdict(set)
298
-
299
- personalization = {}
300
- chat_rel_fnames = set(self.get_rel_fname(f) for f in chat_fnames)
301
-
302
- all_fnames = list(set(chat_fnames + other_fnames))
303
-
304
- for fname in all_fnames:
305
- rel_fname = self.get_rel_fname(fname)
306
-
307
- if not os.path.exists(fname):
308
- reason = "File not found"
309
- excluded[fname] = reason
310
- self.output_handlers['warning'](f"Repo-map can't include {fname}: {reason}")
311
- continue
312
-
313
- included.append(fname)
314
-
315
- tags = self.get_tags(fname, rel_fname)
316
-
317
- for tag in tags:
318
- if tag.kind == "def":
319
- defines[tag.name].add(rel_fname)
320
- definitions[rel_fname].add(tag.name)
321
- total_definitions += 1
322
- elif tag.kind == "ref":
323
- references[tag.name].add(rel_fname)
324
- total_references += 1
325
-
326
- # Set personalization for chat files
327
- if fname in chat_fnames:
328
- personalization[rel_fname] = 100.0
329
-
330
- # Build graph
331
- G = nx.MultiDiGraph()
332
-
333
- # Add nodes
334
- for fname in all_fnames:
335
- rel_fname = self.get_rel_fname(fname)
336
- G.add_node(rel_fname)
337
-
338
- # Add edges based on references
339
- for name, ref_fnames in references.items():
340
- def_fnames = defines.get(name, set())
341
- for ref_fname in ref_fnames:
342
- for def_fname in def_fnames:
343
- if ref_fname != def_fname:
344
- G.add_edge(ref_fname, def_fname, name=name)
345
-
346
- if not G.nodes():
347
- return [], file_report
348
-
349
- # Run PageRank
350
- try:
351
- if personalization:
352
- ranks = nx.pagerank(G, personalization=personalization, alpha=0.85)
353
- else:
354
- ranks = {node: 1.0 for node in G.nodes()}
355
- except:
356
- # Fallback to uniform ranking
357
- ranks = {node: 1.0 for node in G.nodes()}
358
-
359
- # Update excluded dictionary with status information
360
- for fname in set(chat_fnames + other_fnames):
361
- if fname in excluded:
362
- # Add status prefix to existing exclusion reason
363
- excluded[fname] = f"[EXCLUDED] {excluded[fname]}"
364
- elif fname not in included:
365
- excluded[fname] = "[NOT PROCESSED] File not included in final processing"
366
-
367
- # Create file report
368
- file_report = FileReport(
369
- excluded=excluded,
370
- definition_matches=total_definitions,
371
- reference_matches=total_references,
372
- total_files_considered=len(all_fnames)
373
- )
374
-
375
- # Collect and rank tags
376
- ranked_tags = []
377
-
378
- for fname in included:
379
- rel_fname = self.get_rel_fname(fname)
380
- file_rank = ranks.get(rel_fname, 0.0)
381
-
382
- # Exclude files with low Page Rank if exclude_unranked is True
383
- if self.exclude_unranked and file_rank <= 0.0001: # Use a small threshold to exclude near-zero ranks
384
- continue
385
-
386
- tags = self.get_tags(fname, rel_fname)
387
- for tag in tags:
388
- if tag.kind == "def":
389
- # Boost for mentioned identifiers
390
- boost = 1.0
391
- if tag.name in mentioned_idents:
392
- boost *= 10.0
393
- if rel_fname in mentioned_fnames:
394
- boost *= 5.0
395
- if rel_fname in chat_rel_fnames:
396
- boost *= 20.0
397
-
398
- final_rank = file_rank * boost
399
- ranked_tags.append((final_rank, tag))
400
-
401
- # Sort by rank (descending)
402
- ranked_tags.sort(key=lambda x: x[0], reverse=True)
403
-
404
- return ranked_tags, file_report
405
-
406
- def render_tree(self, abs_fname: str, rel_fname: str, lois: List[int]) -> str:
407
- """Render a code snippet with specific lines of interest."""
408
- code = self.read_text_func_internal(abs_fname)
409
- if not code:
410
- return ""
411
-
412
- # Use TreeContext for rendering
413
- try:
414
- if rel_fname not in self.tree_context_cache:
415
- self.tree_context_cache[rel_fname] = TreeContext(
416
- rel_fname,
417
- code,
418
- color=False
419
- )
420
-
421
- tree_context = self.tree_context_cache[rel_fname]
422
- return tree_context.format(lois)
423
- except Exception:
424
- # Fallback to simple line extraction
425
- lines = code.splitlines()
426
- result_lines = [f"{rel_fname}:"]
427
-
428
- for loi in sorted(set(lois)):
429
- if 1 <= loi <= len(lines):
430
- result_lines.append(f"{loi:4d}: {lines[loi-1]}")
431
-
432
- return "\n".join(result_lines)
433
-
434
- def to_tree(self, tags: List[Tuple[float, Tag]], chat_rel_fnames: Set[str]) -> str:
435
- """Convert ranked tags to formatted tree output."""
436
- if not tags:
437
- return ""
438
-
439
- # Group tags by file
440
- file_tags = defaultdict(list)
441
- for rank, tag in tags:
442
- file_tags[tag.rel_fname].append((rank, tag))
443
-
444
- # Sort files by importance (max rank of their tags)
445
- sorted_files = sorted(
446
- file_tags.items(),
447
- key=lambda x: max(rank for rank, tag in x[1]),
448
- reverse=True
449
- )
450
-
451
- tree_parts = []
452
-
453
- for rel_fname, file_tag_list in sorted_files:
454
- # Get lines of interest
455
- lois = [tag.line for rank, tag in file_tag_list]
456
-
457
- # Find absolute filename
458
- abs_fname = str(self.root / rel_fname)
459
-
460
- # Get the max rank for the file
461
- max_rank = max(rank for rank, tag in file_tag_list)
462
-
463
- # Render the tree for this file
464
- rendered = self.render_tree(abs_fname, rel_fname, lois)
465
- if rendered:
466
- # Add rank value to the output
467
- rendered_lines = rendered.splitlines()
468
- first_line = rendered_lines[0]
469
- code_lines = rendered_lines[1:]
470
-
471
- tree_parts.append(
472
- f"{first_line}\n"
473
- f"(Rank value: {max_rank:.4f})\n\n" # Added an extra newline here
474
- + "\n".join(code_lines)
475
- )
476
-
477
- return "\n\n".join(tree_parts)
478
-
479
- def get_ranked_tags_map(
480
- self,
481
- chat_fnames: List[str],
482
- other_fnames: List[str],
483
- max_map_tokens: int,
484
- mentioned_fnames: Optional[Set[str]] = None,
485
- mentioned_idents: Optional[Set[str]] = None,
486
- force_refresh: bool = False
487
- ) -> Tuple[Optional[str], FileReport]:
488
- """Get the ranked tags map with caching."""
489
- cache_key = (
490
- tuple(sorted(chat_fnames)),
491
- tuple(sorted(other_fnames)),
492
- max_map_tokens,
493
- tuple(sorted(mentioned_fnames or [])),
494
- tuple(sorted(mentioned_idents or [])),
495
- )
496
-
497
- if not force_refresh and cache_key in self.map_cache:
498
- return self.map_cache[cache_key]
499
-
500
- result = self.get_ranked_tags_map_uncached(
501
- chat_fnames, other_fnames, max_map_tokens,
502
- mentioned_fnames, mentioned_idents
503
- )
504
-
505
- self.map_cache[cache_key] = result
506
- return result
507
-
508
- def get_ranked_tags_map_uncached(
509
- self,
510
- chat_fnames: List[str],
511
- other_fnames: List[str],
512
- max_map_tokens: int,
513
- mentioned_fnames: Optional[Set[str]] = None,
514
- mentioned_idents: Optional[Set[str]] = None
515
- ) -> Tuple[Optional[str], FileReport]:
516
- """Generate the ranked tags map without caching."""
517
- ranked_tags, file_report = self.get_ranked_tags(
518
- chat_fnames, other_fnames, mentioned_fnames, mentioned_idents
519
- )
520
-
521
- if not ranked_tags:
522
- return None, file_report
523
-
524
- # Filter important files
525
- important_files = filter_important_files(
526
- [self.get_rel_fname(f) for f in other_fnames]
527
- )
528
-
529
- # Binary search to find the right number of tags
530
- chat_rel_fnames = set(self.get_rel_fname(f) for f in chat_fnames)
531
-
532
- def try_tags(num_tags: int) -> Tuple[Optional[str], int]:
533
- if num_tags <= 0:
534
- return None, 0
535
-
536
- selected_tags = ranked_tags[:num_tags]
537
- tree_output = self.to_tree(selected_tags, chat_rel_fnames)
538
-
539
- if not tree_output:
540
- return None, 0
541
-
542
- tokens = self.token_count(tree_output)
543
- return tree_output, tokens
544
-
545
- # Binary search for optimal number of tags
546
- left, right = 0, len(ranked_tags)
547
- best_tree = None
548
-
549
- while left <= right:
550
- mid = (left + right) // 2
551
- tree_output, tokens = try_tags(mid)
552
-
553
- if tree_output and tokens <= max_map_tokens:
554
- best_tree = tree_output
555
- left = mid + 1
556
- else:
557
- right = mid - 1
558
-
559
- return best_tree, file_report
560
-
561
- def get_repo_map(
562
- self,
563
- chat_files: List[str] = None,
564
- other_files: List[str] = None,
565
- mentioned_fnames: Optional[Set[str]] = None,
566
- mentioned_idents: Optional[Set[str]] = None,
567
- force_refresh: bool = False,
568
- show_preview: bool = False
569
- ) -> Tuple[Optional[str], FileReport]:
570
- """Generate the repository map with file report.
571
-
572
- Args:
573
- chat_files: List of chat-related files to prioritize
574
- other_files: List of other files to include
575
- mentioned_fnames: Set of mentioned filenames for boosting
576
- mentioned_idents: Set of mentioned identifiers for boosting
577
- force_refresh: Force refresh of cached data
578
- show_preview: If False, returns "complete" instead of full map content
579
-
580
- Returns:
581
- Tuple of (map_content, file_report) where map_content is either
582
- the full repository map or "complete" based on show_preview setting
583
- """
584
- if chat_files is None:
585
- chat_files = []
586
- if other_files is None:
587
- other_files = []
588
-
589
- # Create empty report for error cases
590
- empty_report = FileReport({}, 0, 0, 0)
591
-
592
- if self.max_map_tokens <= 0 or not other_files:
593
- return None, empty_report
594
-
595
- # Adjust max_map_tokens if no chat files
596
- max_map_tokens = self.max_map_tokens
597
- if not chat_files and self.max_context_window:
598
- padding = 1024
599
- available = self.max_context_window - padding
600
- max_map_tokens = min(
601
- max_map_tokens * self.map_mul_no_files,
602
- available
603
- )
604
-
605
- try:
606
- # get_ranked_tags_map returns (map_string, file_report)
607
- map_string, file_report = self.get_ranked_tags_map(
608
- chat_files, other_files, max_map_tokens,
609
- mentioned_fnames, mentioned_idents, force_refresh
610
- )
611
- except RecursionError:
612
- self.output_handlers['error']("Disabling repo map, git repo too large?")
613
- self.max_map_tokens = 0
614
- return None, FileReport({}, 0, 0, 0) # Ensure consistent return type
615
-
616
- if map_string is None:
617
- return None, file_report
618
-
619
- if self.verbose:
620
- tokens = self.token_count(map_string)
621
- self.output_handlers['info'](f"Repo-map: {tokens / 1024:.1f} k-tokens")
622
-
623
- # Format final output
624
- if not show_preview:
625
- # Return simple completion message instead of full map
626
- return "complete", file_report
627
-
628
- other = "other " if chat_files else ""
629
-
630
- if self.repo_content_prefix:
631
- repo_content = self.repo_content_prefix.format(other=other)
632
- else:
633
- repo_content = ""
634
-
635
- repo_content += map_string
636
-
637
- return repo_content, file_report