token-savior-recall 2.0.0__py3-none-any.whl

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 (58) hide show
  1. token_savior/__init__.py +3 -0
  2. token_savior/annotator.py +113 -0
  3. token_savior/brace_matcher.py +367 -0
  4. token_savior/breaking_changes.py +542 -0
  5. token_savior/c_annotator.py +697 -0
  6. token_savior/cache_ops.py +256 -0
  7. token_savior/checkpoint_ops.py +197 -0
  8. token_savior/community.py +145 -0
  9. token_savior/compact_ops.py +101 -0
  10. token_savior/complexity.py +136 -0
  11. token_savior/conf_annotator.py +94 -0
  12. token_savior/config_analyzer.py +971 -0
  13. token_savior/context_packer.py +120 -0
  14. token_savior/cross_project.py +132 -0
  15. token_savior/csharp_annotator.py +685 -0
  16. token_savior/dashboard.py +981 -0
  17. token_savior/dead_code.py +195 -0
  18. token_savior/docker_analyzer.py +296 -0
  19. token_savior/dockerfile_annotator.py +102 -0
  20. token_savior/edit_ops.py +168 -0
  21. token_savior/edit_verifier.py +161 -0
  22. token_savior/entry_points.py +90 -0
  23. token_savior/env_annotator.py +56 -0
  24. token_savior/generic_annotator.py +21 -0
  25. token_savior/git_ops.py +90 -0
  26. token_savior/git_tracker.py +248 -0
  27. token_savior/go_annotator.py +511 -0
  28. token_savior/graph_ranker.py +109 -0
  29. token_savior/hcl_annotator.py +143 -0
  30. token_savior/impacted_tests.py +261 -0
  31. token_savior/ini_annotator.py +148 -0
  32. token_savior/json_annotator.py +161 -0
  33. token_savior/markov_prefetcher.py +105 -0
  34. token_savior/memory_db.py +2345 -0
  35. token_savior/memory_schema.sql +191 -0
  36. token_savior/models.py +249 -0
  37. token_savior/program_slicer.py +160 -0
  38. token_savior/project_actions.py +274 -0
  39. token_savior/project_indexer.py +1015 -0
  40. token_savior/python_annotator.py +298 -0
  41. token_savior/query_api.py +1460 -0
  42. token_savior/rust_annotator.py +738 -0
  43. token_savior/semantic_hasher.py +92 -0
  44. token_savior/server.py +2259 -0
  45. token_savior/slot_manager.py +373 -0
  46. token_savior/symbol_hash.py +124 -0
  47. token_savior/text_annotator.py +118 -0
  48. token_savior/toml_annotator.py +105 -0
  49. token_savior/tool_schemas.py +1339 -0
  50. token_savior/typescript_annotator.py +568 -0
  51. token_savior/workflow_ops.py +174 -0
  52. token_savior/xml_annotator.py +108 -0
  53. token_savior/yaml_annotator.py +143 -0
  54. token_savior_recall-2.0.0.dist-info/METADATA +470 -0
  55. token_savior_recall-2.0.0.dist-info/RECORD +58 -0
  56. token_savior_recall-2.0.0.dist-info/WHEEL +4 -0
  57. token_savior_recall-2.0.0.dist-info/entry_points.txt +4 -0
  58. token_savior_recall-2.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,3 @@
1
+ """Token Savior — structural code indexer with MCP server for AI-assisted development."""
2
+
3
+ __version__ = "3.0.0"
@@ -0,0 +1,113 @@
1
+ """Dispatch layer that selects the appropriate annotator by file type."""
2
+
3
+ from token_savior.c_annotator import annotate_c
4
+ from token_savior.conf_annotator import annotate_conf
5
+ from token_savior.dockerfile_annotator import annotate_dockerfile
6
+ from token_savior.csharp_annotator import annotate_csharp
7
+ from token_savior.env_annotator import annotate_env
8
+ from token_savior.generic_annotator import annotate_generic
9
+ from token_savior.go_annotator import annotate_go
10
+ from token_savior.hcl_annotator import annotate_hcl
11
+ from token_savior.ini_annotator import annotate_ini
12
+ from token_savior.json_annotator import annotate_json
13
+ from token_savior.models import AnnotatorProtocol, StructuralMetadata
14
+ from token_savior.python_annotator import annotate_python
15
+ from token_savior.rust_annotator import annotate_rust
16
+ from token_savior.text_annotator import annotate_text
17
+ from token_savior.toml_annotator import annotate_toml
18
+ from token_savior.typescript_annotator import annotate_typescript
19
+ from token_savior.xml_annotator import annotate_xml
20
+ from token_savior.yaml_annotator import annotate_yaml
21
+
22
+ _EXTENSION_MAP: dict[str, str] = {
23
+ ".c": "c",
24
+ ".h": "c",
25
+ ".glsl": "c",
26
+ ".vert": "c",
27
+ ".frag": "c",
28
+ ".comp": "c",
29
+ ".py": "python",
30
+ ".pyw": "python",
31
+ ".md": "text",
32
+ ".txt": "text",
33
+ ".rst": "text",
34
+ ".ts": "typescript",
35
+ ".tsx": "typescript",
36
+ ".js": "javascript",
37
+ ".jsx": "javascript",
38
+ ".go": "go",
39
+ ".rs": "rust",
40
+ ".cs": "csharp",
41
+ ".json": "json",
42
+ ".yaml": "yaml",
43
+ ".yml": "yaml",
44
+ ".toml": "toml",
45
+ ".ini": "ini",
46
+ ".cfg": "ini",
47
+ ".properties": "ini",
48
+ ".env": "env",
49
+ ".xml": "xml",
50
+ ".plist": "xml",
51
+ ".svg": "xml",
52
+ ".xhtml": "xml",
53
+ ".hcl": "hcl",
54
+ ".tf": "hcl",
55
+ ".conf": "conf",
56
+ }
57
+
58
+
59
+ _ANNOTATOR_MAP: dict[str, AnnotatorProtocol] = {
60
+ "c": annotate_c,
61
+ "python": annotate_python,
62
+ "text": annotate_text,
63
+ "typescript": annotate_typescript,
64
+ "javascript": annotate_typescript,
65
+ "go": annotate_go,
66
+ "rust": annotate_rust,
67
+ "csharp": annotate_csharp,
68
+ "json": annotate_json,
69
+ "yaml": annotate_yaml,
70
+ "ini": annotate_ini,
71
+ "xml": annotate_xml,
72
+ "hcl": annotate_hcl,
73
+ "toml": annotate_toml,
74
+ "env": annotate_env,
75
+ "conf": annotate_conf,
76
+ "dockerfile": annotate_dockerfile,
77
+ }
78
+
79
+
80
+ def annotate(
81
+ text: str,
82
+ source_name: str = "<source>",
83
+ file_type: str | None = None,
84
+ ) -> StructuralMetadata:
85
+ """Annotate text with structural metadata.
86
+
87
+ Dispatch rules:
88
+ - file_type overrides extension-based detection
89
+ - Extension -> _EXTENSION_MAP -> language name -> _ANNOTATOR_MAP -> annotator
90
+ - Dockerfile detected by filename pattern
91
+ - Otherwise -> generic annotator (line-only)
92
+ """
93
+ if file_type is None:
94
+ import os as _os
95
+
96
+ _basename = _os.path.basename(source_name)
97
+ if (
98
+ _basename == "Dockerfile"
99
+ or _basename.lower() == "dockerfile"
100
+ or _basename.startswith("Dockerfile.")
101
+ or _basename.lower().endswith(".dockerfile")
102
+ ):
103
+ file_type = "dockerfile"
104
+ else:
105
+ dot_idx = source_name.rfind(".")
106
+ if dot_idx >= 0:
107
+ ext = source_name[dot_idx:].lower()
108
+ file_type = _EXTENSION_MAP.get(ext)
109
+
110
+ annotator = _ANNOTATOR_MAP.get(file_type, annotate_generic)
111
+ if not callable(annotator):
112
+ raise TypeError(f"Annotator for '{file_type}' is not callable: {annotator!r}")
113
+ return annotator(text, source_name)
@@ -0,0 +1,367 @@
1
+ """Shared brace-matching helpers for C-family annotators.
2
+
3
+ Each function finds the 0-based line where the outermost ``{`` closes,
4
+ correctly skipping language-specific strings, char literals, and comments.
5
+ The implementations are intentionally separate because the lexical rules
6
+ differ significantly between languages (nested comments in Rust, verbatim
7
+ strings in C#, backtick raw strings in Go, etc.).
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+
13
+ def find_brace_end_c(lines: list[str], start_line_0: int) -> int:
14
+ """Find the 0-based line where the outermost brace closes,
15
+ skipping strings, char literals, and comments."""
16
+ depth = 0
17
+ found_open = False
18
+ in_block_comment = False
19
+ for idx in range(start_line_0, len(lines)):
20
+ line = lines[idx]
21
+ i = 0
22
+ while i < len(line):
23
+ ch = line[i]
24
+ # Block comment handling (C does NOT nest /* */)
25
+ if in_block_comment:
26
+ if ch == "*" and i + 1 < len(line) and line[i + 1] == "/":
27
+ in_block_comment = False
28
+ i += 2
29
+ continue
30
+ i += 1
31
+ continue
32
+ # Line comment
33
+ if ch == "/" and i + 1 < len(line):
34
+ if line[i + 1] == "/":
35
+ break # rest is line comment
36
+ if line[i + 1] == "*":
37
+ in_block_comment = True
38
+ i += 2
39
+ continue
40
+ # String literal
41
+ if ch == '"':
42
+ i += 1
43
+ while i < len(line):
44
+ if line[i] == "\\":
45
+ i += 2
46
+ continue
47
+ if line[i] == '"':
48
+ i += 1
49
+ break
50
+ i += 1
51
+ continue
52
+ # Char literal
53
+ if ch == "'":
54
+ i += 1
55
+ if i < len(line) and line[i] == "\\":
56
+ i += 2 # skip escaped char
57
+ elif i < len(line):
58
+ i += 1 # skip char
59
+ if i < len(line) and line[i] == "'":
60
+ i += 1
61
+ continue
62
+ if ch == "{":
63
+ depth += 1
64
+ found_open = True
65
+ elif ch == "}":
66
+ depth -= 1
67
+ if found_open and depth == 0:
68
+ return idx
69
+ i += 1
70
+ return len(lines) - 1
71
+
72
+
73
+ def find_brace_end_csharp(lines: list[str], start_line_0: int) -> int:
74
+ """Find the 0-based line where the outermost brace closes,
75
+ skipping strings, verbatim strings, interpolated strings, char literals, and comments."""
76
+ depth = 0
77
+ found_open = False
78
+ in_block_comment = False
79
+ for idx in range(start_line_0, len(lines)):
80
+ line = lines[idx]
81
+ i = 0
82
+ while i < len(line):
83
+ ch = line[i]
84
+ # Block comment handling
85
+ if in_block_comment:
86
+ if ch == "*" and i + 1 < len(line) and line[i + 1] == "/":
87
+ in_block_comment = False
88
+ i += 2
89
+ continue
90
+ i += 1
91
+ continue
92
+ # Line comment
93
+ if ch == "/" and i + 1 < len(line):
94
+ if line[i + 1] == "/":
95
+ break # rest is line comment
96
+ if line[i + 1] == "*":
97
+ in_block_comment = True
98
+ i += 2
99
+ continue
100
+ # Verbatim/interpolated strings: $@"...", @$"...", @"...", $"..."
101
+ if ch in ("@", "$") and i + 1 < len(line):
102
+ # Check for $@" or @$" (interpolated verbatim)
103
+ if (
104
+ ch == "$" and line[i + 1] == "@" and i + 2 < len(line) and line[i + 2] == '"'
105
+ ) or (
106
+ ch == "@" and line[i + 1] == "$" and i + 2 < len(line) and line[i + 2] == '"'
107
+ ):
108
+ # Interpolated verbatim string — "" for escaped quote
109
+ i += 3
110
+ while i < len(line):
111
+ if line[i] == '"':
112
+ if i + 1 < len(line) and line[i + 1] == '"':
113
+ i += 2
114
+ continue
115
+ i += 1
116
+ break
117
+ i += 1
118
+ else:
119
+ # Multi-line verbatim string
120
+ idx += 1
121
+ while idx < len(lines):
122
+ line = lines[idx]
123
+ i = 0
124
+ while i < len(line):
125
+ if line[i] == '"':
126
+ if i + 1 < len(line) and line[i + 1] == '"':
127
+ i += 2
128
+ continue
129
+ i += 1
130
+ break
131
+ i += 1
132
+ else:
133
+ idx += 1
134
+ continue
135
+ break
136
+ else:
137
+ return len(lines) - 1
138
+ continue
139
+ # Verbatim string: @"..."
140
+ if ch == "@" and line[i + 1] == '"':
141
+ i += 2
142
+ while i < len(line):
143
+ if line[i] == '"':
144
+ if i + 1 < len(line) and line[i + 1] == '"':
145
+ i += 2
146
+ continue
147
+ i += 1
148
+ break
149
+ i += 1
150
+ else:
151
+ # Multi-line verbatim
152
+ idx += 1
153
+ while idx < len(lines):
154
+ line = lines[idx]
155
+ i = 0
156
+ while i < len(line):
157
+ if line[i] == '"':
158
+ if i + 1 < len(line) and line[i + 1] == '"':
159
+ i += 2
160
+ continue
161
+ i += 1
162
+ break
163
+ i += 1
164
+ else:
165
+ idx += 1
166
+ continue
167
+ break
168
+ else:
169
+ return len(lines) - 1
170
+ continue
171
+ # Interpolated string: $"..."
172
+ if ch == "$" and line[i + 1] == '"':
173
+ i += 2
174
+ while i < len(line):
175
+ if line[i] == "\\":
176
+ i += 2
177
+ continue
178
+ if line[i] == '"':
179
+ i += 1
180
+ break
181
+ i += 1
182
+ continue
183
+ # Regular string
184
+ if ch == '"':
185
+ i += 1
186
+ while i < len(line):
187
+ if line[i] == "\\":
188
+ i += 2
189
+ continue
190
+ if line[i] == '"':
191
+ i += 1
192
+ break
193
+ i += 1
194
+ continue
195
+ # Char literal
196
+ if ch == "'" and i + 1 < len(line):
197
+ if i + 2 < len(line) and line[i + 1] == "\\":
198
+ end = line.find("'", i + 2)
199
+ if end >= 0 and end <= i + 4:
200
+ i = end + 1
201
+ continue
202
+ elif i + 2 < len(line) and line[i + 2] == "'":
203
+ i += 3
204
+ continue
205
+ if ch == "{":
206
+ depth += 1
207
+ found_open = True
208
+ elif ch == "}":
209
+ depth -= 1
210
+ if found_open and depth == 0:
211
+ return idx
212
+ i += 1
213
+ return len(lines) - 1
214
+
215
+
216
+ def find_brace_end_rust(lines: list[str], start_line_0: int) -> int:
217
+ """Find the 0-based line where the outermost brace closes,
218
+ skipping strings, raw strings, char literals, and comments."""
219
+ depth = 0
220
+ found_open = False
221
+ in_block_comment = 0 # nesting depth for /* */
222
+ for idx in range(start_line_0, len(lines)):
223
+ line = lines[idx]
224
+ i = 0
225
+ while i < len(line):
226
+ ch = line[i]
227
+ # Block comment handling (Rust supports nested /* */)
228
+ if in_block_comment > 0:
229
+ if ch == "/" and i + 1 < len(line) and line[i + 1] == "*":
230
+ in_block_comment += 1
231
+ i += 2
232
+ continue
233
+ if ch == "*" and i + 1 < len(line) and line[i + 1] == "/":
234
+ in_block_comment -= 1
235
+ i += 2
236
+ continue
237
+ i += 1
238
+ continue
239
+ # Line comment
240
+ if ch == "/" and i + 1 < len(line):
241
+ if line[i + 1] == "/":
242
+ break # rest is line comment
243
+ if line[i + 1] == "*":
244
+ in_block_comment += 1
245
+ i += 2
246
+ continue
247
+ # Raw string: r#"..."#, r##"..."##, etc.
248
+ if ch == "r" and i + 1 < len(line) and line[i + 1] in ('"', "#"):
249
+ hash_count = 0
250
+ j = i + 1
251
+ while j < len(line) and line[j] == "#":
252
+ hash_count += 1
253
+ j += 1
254
+ if j < len(line) and line[j] == '"':
255
+ j += 1
256
+ # Find closing "###
257
+ closing = '"' + "#" * hash_count
258
+ while True:
259
+ pos = line.find(closing, j)
260
+ if pos >= 0:
261
+ i = pos + len(closing)
262
+ break
263
+ # Span to next line
264
+ idx += 1
265
+ if idx >= len(lines):
266
+ return len(lines) - 1
267
+ line = lines[idx]
268
+ j = 0
269
+ continue
270
+ # Regular string
271
+ if ch == '"':
272
+ i += 1
273
+ while i < len(line):
274
+ if line[i] == "\\":
275
+ i += 2
276
+ continue
277
+ if line[i] == '"':
278
+ i += 1
279
+ break
280
+ i += 1
281
+ continue
282
+ # Char literal (skip 'a', '\n', etc. but not lifetime 'a)
283
+ if ch == "'" and i + 1 < len(line):
284
+ # Lifetime check: 'a where next is alpha and followed by non-'
285
+ # Char literal: 'x' or '\n'
286
+ if i + 2 < len(line) and line[i + 1] == "\\":
287
+ # Escaped char literal like '\n'
288
+ end = line.find("'", i + 2)
289
+ if end >= 0 and end <= i + 4:
290
+ i = end + 1
291
+ continue
292
+ elif i + 2 < len(line) and line[i + 2] == "'":
293
+ # Simple char literal like 'a'
294
+ i += 3
295
+ continue
296
+ # Otherwise it's a lifetime, skip
297
+ if ch == "{":
298
+ depth += 1
299
+ found_open = True
300
+ elif ch == "}":
301
+ depth -= 1
302
+ if found_open and depth == 0:
303
+ return idx
304
+ i += 1
305
+ return len(lines) - 1
306
+
307
+
308
+ def find_brace_end_go(lines: list[str], start_line_0: int) -> int:
309
+ """Find the 0-based line where the outermost brace closes, skipping strings/comments."""
310
+ depth = 0
311
+ found_open = False
312
+ in_block_comment = False
313
+ for idx in range(start_line_0, len(lines)):
314
+ line = lines[idx]
315
+ i = 0
316
+ while i < len(line):
317
+ ch = line[i]
318
+ if in_block_comment:
319
+ if ch == "*" and i + 1 < len(line) and line[i + 1] == "/":
320
+ in_block_comment = False
321
+ i += 2
322
+ continue
323
+ i += 1
324
+ continue
325
+ if ch == "/" and i + 1 < len(line):
326
+ if line[i + 1] == "/":
327
+ break # rest is line comment
328
+ if line[i + 1] == "*":
329
+ in_block_comment = True
330
+ i += 2
331
+ continue
332
+ if ch == '"':
333
+ i += 1
334
+ while i < len(line) and line[i] != '"':
335
+ if line[i] == "\\":
336
+ i += 1
337
+ i += 1
338
+ i += 1
339
+ continue
340
+ if ch == "`":
341
+ # raw string can span lines - scan to end
342
+ i += 1
343
+ while True:
344
+ while i < len(line):
345
+ if line[i] == "`":
346
+ i += 1
347
+ break
348
+ i += 1
349
+ else:
350
+ # continue to next line
351
+ idx += 1
352
+ if idx >= len(lines):
353
+ return len(lines) - 1
354
+ line = lines[idx]
355
+ i = 0
356
+ continue
357
+ break
358
+ continue
359
+ if ch == "{":
360
+ depth += 1
361
+ found_open = True
362
+ elif ch == "}":
363
+ depth -= 1
364
+ if found_open and depth == 0:
365
+ return idx
366
+ i += 1
367
+ return len(lines) - 1