sourcecode 2.5.4__py3-none-any.whl → 2.5.6__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.
sourcecode/__init__.py CHANGED
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "2.5.4"
7
+ __version__ = "2.5.6"
@@ -4,6 +4,12 @@ import re
4
4
  from pathlib import Path
5
5
  from typing import Literal, Optional
6
6
 
7
+ from sourcecode.reconciliation import (
8
+ incoherent_layers,
9
+ pattern_contradicted,
10
+ unattested_layers,
11
+ )
12
+
7
13
  from sourcecode.schema import (
8
14
  ArchitectureAnalysis,
9
15
  ArchitectureDomain,
@@ -39,11 +45,27 @@ _SRC_TRANSPARENT = {
39
45
  "src", "lib", "app", "pkg",
40
46
  "main", "java", "kotlin", "scala", "groovy",
41
47
  }
48
+
49
+ # `src/main/java17/` is a MULTI-RELEASE source set, as transparent as `java/`
50
+ # — JobRunr's minted a module called "java17".
51
+ _VERSIONED_SRC_RE = re.compile(r"^(?:java|kotlin|scala)\d+$")
52
+
53
+
54
+ def _is_transparent_segment(name: str) -> bool:
55
+ """A path segment that carries no architectural meaning of its own."""
56
+ low = name.lower()
57
+ return low in _SRC_TRANSPARENT or bool(_VERSIONED_SRC_RE.match(low))
42
58
  _CODE_EXTENSIONS = {
43
59
  ".py", ".js", ".ts", ".tsx", ".jsx", ".mjs", ".cjs",
44
60
  ".go", ".java", ".kt", ".rs", ".rb",
45
61
  }
46
- _GENERIC_NAMES = {"utils", "helpers", "common", "shared", "misc", "core", "root", ""}
62
+ # Names that never identify a module: generic buckets, and the reverse-DNS
63
+ # package roots every JVM repo starts its namespace with (JobRunr reported a
64
+ # module called "org").
65
+ _GENERIC_NAMES = {
66
+ "utils", "helpers", "common", "shared", "misc", "core", "root", "",
67
+ "org", "com", "io", "net",
68
+ }
47
69
 
48
70
  _TEST_DIRS: frozenset[str] = frozenset({
49
71
  "tests", "test", "spec", "specs", "__tests__", "e2e",
@@ -139,8 +161,14 @@ LAYER_PATTERNS: dict[str, dict[str, list[str]]] = {
139
161
  "application": ["application", "usecases", "usecase"],
140
162
  "infrastructure": ["infrastructure", "infra", "adapters", "persistence"],
141
163
  },
164
+ # P1-D residue (found by measurement, 2026-07-16): "core" belongs with
165
+ # "api"/"store"/"db" — it names a module's importance ("the main module"),
166
+ # never an onion/hexagonal DOMAIN layer, and nearly every large JVM repo has
167
+ # one. With it, keycloak's `core/` (623 Java files) + a
168
+ # `representations/adapters/config` package read "Onion Architecture with
169
+ # domain, adapters layers". Kept are the names that state their role.
142
170
  "onion": {
143
- "domain": ["domain", "core"],
171
+ "domain": ["domain"],
144
172
  "application": ["application", "usecases"],
145
173
  "ports": ["ports", "interfaces"],
146
174
  "adapters": ["adapters", "secondary"],
@@ -173,7 +201,7 @@ LAYER_PATTERNS: dict[str, dict[str, list[str]]] = {
173
201
  "hexagonal": {
174
202
  "port": ["port", "ports", "interface", "interfaces"],
175
203
  "adapter": ["adapter", "adapters"],
176
- "domain": ["domain", "core", "model", "models"],
204
+ "domain": ["domain", "model", "models"], # "core" dropped — see onion
177
205
  },
178
206
  "monorepo": {
179
207
  "apps": ["apps", "applications"],
@@ -196,6 +224,13 @@ LAYER_PATTERNS: dict[str, dict[str, list[str]]] = {
196
224
  # it; without it the pattern falls through to layered / a weaker match.
197
225
  _PATTERN_REQUIRED_KEYS: dict[str, frozenset[str]] = {
198
226
  "mvc": frozenset({"view"}),
227
+ # Same principle as MVC/view — a pattern must show its DEFINING trait, not
228
+ # just any two of its layers. Hexagonal is literally "Ports and Adapters":
229
+ # a `models/` package next to an `adapters/` package is not one, and that
230
+ # pair alone was enough to label keycloak "Hexagonal Architecture". Onion's
231
+ # defining trait is the domain at the centre.
232
+ "hexagonal": frozenset({"port", "adapter"}),
233
+ "onion": frozenset({"domain"}),
199
234
  }
200
235
 
201
236
  # Minimum share of source files a matched layer must hold to count as a LAYER
@@ -646,6 +681,28 @@ class ArchitectureAnalyzer:
646
681
  continue
647
682
  matched[layer_key] = matched_dirs
648
683
  layer_files[layer_key] = files
684
+
685
+ # SIM-3: a layer must belong to the same PROGRAM as the rest of the
686
+ # claim. Directory names are language-blind, so on a polyglot
687
+ # monorepo one pattern was assembled across two subsystems: keycloak
688
+ # read "MVC with controller, model, view layers" where `view` was a
689
+ # React SPA (87 .tsx, zero Java) sitting beside a 1088-file Java
690
+ # `model`. Mass cannot catch this — the SPA has genuine mass. The
691
+ # reconciliation rule drops layers owned by a different runtime;
692
+ # template/asset-only layers are untouched, so a real Spring-MVC
693
+ # `templates/` view survives.
694
+ _layer_paths = {k: [source_paths[i] for i in v] for k, v in layer_files.items()}
695
+ for _bad in incoherent_layers(pattern_name, _layer_paths, source_paths):
696
+ matched.pop(_bad, None)
697
+ layer_files.pop(_bad, None)
698
+
699
+ # A claim that IS a runtime split must show one: JobRunr, a JVM
700
+ # library, read "Fullstack with frontend, backend layers" off a
701
+ # `dashboard/ui/model/` package of 16 Java files. Nothing to prune —
702
+ # the pattern itself is what the evidence contradicts.
703
+ if pattern_contradicted(pattern_name, _layer_paths, source_paths):
704
+ continue
705
+
649
706
  # A pattern with unmet required keys cannot qualify (e.g. mvc needs a view).
650
707
  required = _PATTERN_REQUIRED_KEYS.get(pattern_name)
651
708
  if required and not required.issubset(matched.keys()):
@@ -809,6 +866,14 @@ class ArchitectureAnalyzer:
809
866
  k: v for k, v in layer_files.items()
810
867
  if v and (not total or len(v) / total >= _LAYER_MIN_SHARE)
811
868
  }
869
+ # P1-D residue: these stem patterns are Python-shaped (cli.py,
870
+ # *_analyzer.py, config.py). On a Java repo almost nothing matches, so
871
+ # the few stray hits are not a layering — petclinic's "Layered
872
+ # Architecture with orchestration, data layers" rested on ONE directory
873
+ # entry plus a settings.gradle, with 47 Java files visible and ignored.
874
+ # A logical code layer that holds no code of this repo is not one.
875
+ for _bad in unattested_layers(non_empty, paths):
876
+ non_empty.pop(_bad, None)
812
877
  if len(non_empty) >= 2:
813
878
  return "layered", [
814
879
  ArchitectureLayer(name=k, pattern="layered", files=v, confidence="low")
@@ -828,13 +893,19 @@ class ArchitectureAnalyzer:
828
893
  for p in paths:
829
894
  parts = p.replace("\\", "/").split("/")
830
895
  for part in parts[:-1]:
831
- if (part not in _SRC_TRANSPARENT
896
+ if (not _is_transparent_segment(part)
832
897
  and part.lower() not in _NON_SOURCE_DIRS
833
898
  and part.lower() not in _GENERIC_NAMES):
834
899
  module_files.setdefault(part, []).append(p)
835
900
  break
836
901
 
837
902
  meaningful = {k: v for k, v in module_files.items() if len(v) >= 3}
903
+ # P1-D residue: a MODULE of this repo must hold this repo's code. File
904
+ # count alone let build and asset directories be reported as modules —
905
+ # ofbiz "docker, framework, runtime, gradle", neo4j "packaging",
906
+ # eureka "images" (five PNGs). The real modules survive untouched.
907
+ for _bad in unattested_layers(meaningful, paths):
908
+ meaningful.pop(_bad, None)
838
909
  if len(meaningful) >= 2:
839
910
  return "modular", [
840
911
  ArchitectureLayer(name=k, pattern="modular", files=v, confidence="low")
@@ -34,12 +34,21 @@ Rule registry (extraction):
34
34
  declarations the regex extractor cannot bind, e.g. `public class\n
35
35
  Name` split across lines, Unicode type identifiers).
36
36
 
37
+ Rule registry (architecture):
38
+ layer_language_coherence — a directory-name-matched layer holds NO file in
39
+ the runtime that carries the rest of the claimed pattern, while being
40
+ owned by a DIFFERENT runtime. The layer table and the file-language
41
+ census are contradicting each other: the "architecture" spans two
42
+ subsystems (SIM-3: keycloak's "MVC … view layer" was a React SPA, zero
43
+ Java files, sitting beside a 1088-file Java model).
44
+
37
45
  Never raises. Empty evidence → no findings.
38
46
  """
39
47
  from __future__ import annotations
40
48
 
41
49
  import re
42
50
  from dataclasses import dataclass
51
+ from pathlib import Path
43
52
 
44
53
 
45
54
  @dataclass(frozen=True)
@@ -182,6 +191,253 @@ def _rule_parse_coverage(
182
191
  ]
183
192
 
184
193
 
194
+ # ---------------------------------------------------------------------------
195
+ # Architecture reconciliation (layer table × file-language census)
196
+ # ---------------------------------------------------------------------------
197
+
198
+ # Source extension → RUNTIME FAMILY. Grouping by runtime, not by extension, is
199
+ # what makes the rule correct: a Kotlin `view/` beside Java `model/` is ONE
200
+ # system (same runtime, direct interop) and must not be called a contradiction,
201
+ # while a TypeScript `view/` beside them is a separate program. Extensions
202
+ # absent here (.html/.ftl/.jsp/.css/.xml …) are deliberately unmapped: a
203
+ # template or asset carries no runtime of its own, so it can never contradict a
204
+ # claim — that carve-out is what keeps a genuine Spring-MVC `templates/` view
205
+ # layer intact.
206
+ _RUNTIME_FAMILY: dict[str, str] = {
207
+ ".java": "jvm", ".kt": "jvm", ".kts": "jvm", ".scala": "jvm", ".groovy": "jvm",
208
+ ".js": "js", ".jsx": "js", ".ts": "js", ".tsx": "js", ".mjs": "js", ".cjs": "js",
209
+ ".py": "python",
210
+ ".go": "go",
211
+ ".rs": "rust",
212
+ ".rb": "ruby",
213
+ ".cs": "dotnet",
214
+ ".php": "php",
215
+ ".swift": "swift",
216
+ }
217
+
218
+ # Patterns that describe REPO COMPOSITION rather than one program's internal
219
+ # architecture — their layers are SUPPOSED to span runtimes, so the coherence
220
+ # rule must never fire on them. (mvc/layered/onion/hexagonal/clean are claims
221
+ # about a single program and must hold together in one runtime.)
222
+ _CROSS_RUNTIME_PATTERNS: frozenset[str] = frozenset({
223
+ "fullstack", "microservices", "monorepo",
224
+ })
225
+
226
+ # Patterns whose meaning IS the runtime split — a "fullstack" repo names a
227
+ # frontend TIER and a backend TIER. If every layer runs on the same runtime
228
+ # there is no split, and the claim is a directory-name coincidence: JobRunr, a
229
+ # JVM library, read "Fullstack with frontend, backend layers" off a
230
+ # `dashboard/ui/model/` package holding 16 JAVA files. (Not monorepo or
231
+ # microservices: an all-TypeScript monorepo, or same-runtime services, are
232
+ # perfectly real.)
233
+ _SPLIT_REQUIRED_PATTERNS: frozenset[str] = frozenset({"fullstack"})
234
+
235
+
236
+ def runtime_census(layer_files: dict[str, list[str]]) -> dict[str, dict[str, int]]:
237
+ """{layer -> {runtime_family -> file count}} — the language evidence view.
238
+
239
+ Files whose extension carries no runtime (templates, assets, config) are
240
+ simply absent from the census; a layer made only of them yields {}.
241
+ """
242
+ out: dict[str, dict[str, int]] = {}
243
+ for layer, files in layer_files.items():
244
+ counts: dict[str, int] = {}
245
+ for f in files:
246
+ fam = _RUNTIME_FAMILY.get(Path(str(f)).suffix.lower())
247
+ if fam:
248
+ counts[fam] = counts.get(fam, 0) + 1
249
+ out[layer] = counts
250
+ return out
251
+
252
+
253
+ def _rule_layer_language_coherence(
254
+ pattern: str,
255
+ census: dict[str, dict[str, int]],
256
+ repo_runtimes: dict[str, int],
257
+ ) -> list[ReconciliationFinding]:
258
+ """A claimed layer is owned by a different runtime than the repo it describes.
259
+
260
+ The dominant runtime is taken from the WHOLE repo, not from the matched
261
+ layers: which directories happen to match a keyword table is exactly the
262
+ thing under suspicion, so it cannot also be the arbiter. (Measured: on a
263
+ Java backend beside a React SPA, layer-local dominance can invert — the SPA
264
+ out-matches the backend's one matched layer and the JAVA layer gets flagged.)
265
+
266
+ A layer is INCOHERENT iff it holds ZERO files of the repo's runtime AND at
267
+ least one file of another — it is a different program, not a facet of this
268
+ one. A layer with no runtime files at all (templates, assets) is never
269
+ flagged: it does not contradict, it just does not attest.
270
+
271
+ Silent for cross-runtime patterns, whose layers are meant to span runtimes.
272
+ """
273
+ if pattern in _CROSS_RUNTIME_PATTERNS or len(census) < 2:
274
+ return []
275
+ if len(repo_runtimes) < 2:
276
+ return [] # single-runtime repo — nothing to contradict
277
+
278
+ dominant = max(sorted(repo_runtimes), key=lambda f: repo_runtimes[f])
279
+
280
+ incoherent = sorted(
281
+ layer for layer, counts in census.items()
282
+ if not counts.get(dominant) and any(counts.values())
283
+ )
284
+ if not incoherent:
285
+ return []
286
+ return [
287
+ ReconciliationFinding(
288
+ rule="layer_language_coherence",
289
+ symbols=tuple(incoherent),
290
+ detail=(
291
+ f"{len(incoherent)} layer(s) of the '{pattern}' claim hold no "
292
+ f"{dominant} file while the rest of the pattern is {dominant} — "
293
+ "the claim spans two separate programs, so it does not describe "
294
+ "one architecture."
295
+ ),
296
+ )
297
+ ]
298
+
299
+
300
+ def _rule_layer_code_attestation(
301
+ census: dict[str, dict[str, int]],
302
+ repo_runtimes: dict[str, int],
303
+ ) -> list[ReconciliationFinding]:
304
+ """A claimed code module holds no code of the repo's runtime.
305
+
306
+ Stricter sibling of `layer_language_coherence`, for claims whose layers are
307
+ modules or logical code layers — their whole role is to hold code, so a
308
+ directory with none is not one of them. (`layer_language_coherence` cannot
309
+ serve here: it deliberately ignores layers with no runtime files at all, so
310
+ that a real Spring-MVC `templates/` view survives. A view may be templates;
311
+ a MODULE may not.)
312
+
313
+ Measured on the field-test repos: ofbiz's "modular architecture" listed
314
+ `docker`, `gradle`, `runtime` and `themes` (shell scripts, wrapper jars, a
315
+ .gitignore, images — zero Java); neo4j listed `packaging`; eureka listed
316
+ `images` (five PNGs); petclinic's whole "Layered Architecture with
317
+ orchestration, data layers" rested on one directory entry and a
318
+ settings.gradle.
319
+ """
320
+ if not repo_runtimes:
321
+ return []
322
+ dominant = max(sorted(repo_runtimes), key=lambda f: repo_runtimes[f])
323
+ unattested = sorted(
324
+ layer for layer, counts in census.items() if not counts.get(dominant)
325
+ )
326
+ if not unattested:
327
+ return []
328
+ return [
329
+ ReconciliationFinding(
330
+ rule="layer_code_attestation",
331
+ symbols=tuple(unattested),
332
+ detail=(
333
+ f"{len(unattested)} claimed layer(s) hold no {dominant} code — "
334
+ "a directory without the repo's code is not a module of it."
335
+ ),
336
+ )
337
+ ]
338
+
339
+
340
+ def _rule_composition_runtime_split(
341
+ pattern: str,
342
+ census: dict[str, dict[str, int]],
343
+ ) -> list[ReconciliationFinding]:
344
+ """A pattern that names a runtime split shows none.
345
+
346
+ The mirror image of `layer_language_coherence`: a claim about one program
347
+ must NOT span runtimes; a claim that IS the split must show it. Silent
348
+ unless the pattern's layers carry runtime evidence at all.
349
+ """
350
+ if pattern not in _SPLIT_REQUIRED_PATTERNS or len(census) < 2:
351
+ return []
352
+ families = {fam for counts in census.values() for fam in counts}
353
+ if len(families) != 1:
354
+ return [] # a real split, or no runtime evidence to judge
355
+ only = next(iter(families))
356
+ return [
357
+ ReconciliationFinding(
358
+ rule="composition_runtime_split",
359
+ symbols=tuple(sorted(census)),
360
+ detail=(
361
+ f"the '{pattern}' claim names a runtime split but every layer is "
362
+ f"{only} — the layer names are a coincidence, not two tiers."
363
+ ),
364
+ )
365
+ ]
366
+
367
+
368
+ def unattested_layers(
369
+ layer_files: dict[str, list[str]], repo_files: "list[str]"
370
+ ) -> frozenset[str]:
371
+ """Layers holding no code of the repo's runtime (convenience view).
372
+
373
+ For consumers claiming code modules / logical code layers. Never raises.
374
+ """
375
+ try:
376
+ repo_runtimes = runtime_census({"_repo": list(repo_files)})["_repo"]
377
+ out: set[str] = set()
378
+ for f in _rule_layer_code_attestation(runtime_census(layer_files), repo_runtimes):
379
+ out.update(f.symbols)
380
+ return frozenset(out)
381
+ except Exception:
382
+ return frozenset()
383
+
384
+
385
+ def reconcile_architecture_evidence(
386
+ *,
387
+ pattern: str,
388
+ layer_files: dict[str, list[str]],
389
+ repo_files: "list[str]",
390
+ ) -> list[ReconciliationFinding]:
391
+ """Run architecture reconciliation rules. Never raises.
392
+
393
+ Args:
394
+ pattern: candidate pattern name (e.g. "mvc").
395
+ layer_files: {layer -> files matched for that layer}.
396
+ repo_files: every source file considered — supplies the repo's own
397
+ runtime, the arbiter of which layers belong to the claim.
398
+ """
399
+ findings: list[ReconciliationFinding] = []
400
+ try:
401
+ census = runtime_census(layer_files)
402
+ repo_runtimes = runtime_census({"_repo": list(repo_files)})["_repo"]
403
+ findings.extend(
404
+ _rule_layer_language_coherence(pattern, census, repo_runtimes)
405
+ )
406
+ findings.extend(_rule_composition_runtime_split(pattern, census))
407
+ except Exception:
408
+ pass
409
+ return findings
410
+
411
+
412
+ def pattern_contradicted(
413
+ pattern: str, layer_files: dict[str, list[str]], repo_files: "list[str]"
414
+ ) -> bool:
415
+ """True when a rule rejects the PATTERN itself, not just some of its layers.
416
+
417
+ `incoherent_layers` prunes layers and lets the pattern re-qualify on what
418
+ is left; a split-less "fullstack" has nothing to re-qualify with — the
419
+ claim as a whole is the thing contradicted.
420
+ """
421
+ return any(
422
+ f.rule == "composition_runtime_split"
423
+ for f in reconcile_architecture_evidence(
424
+ pattern=pattern, layer_files=layer_files, repo_files=repo_files
425
+ )
426
+ )
427
+
428
+
429
+ def incoherent_layers(
430
+ pattern: str, layer_files: dict[str, list[str]], repo_files: "list[str]"
431
+ ) -> frozenset[str]:
432
+ """Layer names the coherence rule rejects for `pattern` (convenience view)."""
433
+ out: set[str] = set()
434
+ for f in reconcile_architecture_evidence(
435
+ pattern=pattern, layer_files=layer_files, repo_files=repo_files
436
+ ):
437
+ out.update(f.symbols)
438
+ return frozenset(out)
439
+
440
+
185
441
  def reconcile_extraction_evidence(
186
442
  *,
187
443
  type_decl_files: frozenset[str],
sourcecode/summarizer.py CHANGED
@@ -8,7 +8,8 @@ No API calls — templates applied directly over SourceMap.
8
8
  from pathlib import Path
9
9
  from typing import Any
10
10
 
11
- from sourcecode.architecture_analyzer import _LAYER_MIN_SHARE
11
+ from sourcecode.architecture_analyzer import _LAYER_MIN_SHARE, _PATTERN_REQUIRED_KEYS
12
+ from sourcecode.reconciliation import incoherent_layers, pattern_contradicted
12
13
  from sourcecode.detectors.parsers import load_json_file, load_toml_file
13
14
  from sourcecode.tree_utils import safe_read_text
14
15
  from sourcecode.entrypoint_classifier import is_production_entry_point
@@ -62,7 +63,9 @@ _ARCH_LAYER_PATTERNS: dict[str, dict[str, list[str]]] = {
62
63
  "hexagonal": {
63
64
  "port": ["ports", "interfaces"],
64
65
  "adapter": ["adapters"],
65
- "domain": ["domain", "core", "models"],
66
+ # P1-D residue: "core" names a module's importance, not a domain layer
67
+ # (keycloak `core/` = 623 Java files). Keep in step with the analyzer.
68
+ "domain": ["domain", "models"],
66
69
  },
67
70
  "fullstack": {
68
71
  "frontend": ["frontend", "client", "web", "ui", "pages", "components"],
@@ -415,16 +418,35 @@ class ProjectSummarizer:
415
418
  dir_files.setdefault(seg.lower(), set()).add(i)
416
419
  total = len(file_paths)
417
420
 
418
- def _has_mass(keywords: list[str]) -> bool:
421
+ def _layer_paths(keywords: list[str]) -> list[str]:
419
422
  files: set[int] = set()
420
423
  for d in keywords:
421
424
  files |= dir_files.get(d, set())
422
- return bool(files) and (not total or len(files) / total >= _LAYER_MIN_SHARE)
425
+ if not files or (total and len(files) / total < _LAYER_MIN_SHARE):
426
+ return []
427
+ return [file_paths[i] for i in sorted(files)]
423
428
 
424
429
  best_pattern: str | None = None
425
430
  best_score = 0
426
431
  for pattern_name, layer_keys in _ARCH_LAYER_PATTERNS.items():
427
- score = sum(1 for keywords in layer_keys.values() if _has_mass(keywords))
432
+ layer_files: dict[str, list[str]] = {}
433
+ for layer, keywords in layer_keys.items():
434
+ paths = _layer_paths(keywords)
435
+ if paths:
436
+ layer_files[layer] = paths
437
+ # SIM-3: same coherence rule the architecture prose applies — this
438
+ # table is the headline's copy, and a fix applied to only one of the
439
+ # two duplicated tables leaves the other one lying.
440
+ for _bad in incoherent_layers(pattern_name, layer_files, file_paths):
441
+ layer_files.pop(_bad, None)
442
+ if pattern_contradicted(pattern_name, layer_files, file_paths):
443
+ continue
444
+ # A pattern must show its defining trait here too, or the headline
445
+ # contradicts the architecture prose that already enforces this.
446
+ required = _PATTERN_REQUIRED_KEYS.get(pattern_name)
447
+ if required and not required.issubset(layer_files.keys()):
448
+ continue
449
+ score = len(layer_files)
428
450
  if score > best_score:
429
451
  best_score = score
430
452
  best_pattern = pattern_name
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 2.5.4
3
+ Version: 2.5.6
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -1,7 +1,7 @@
1
- sourcecode/__init__.py,sha256=XumauaLNdzXEwNGsJupjZthcLY2Qo8ud3CgsBguv8-o,308
1
+ sourcecode/__init__.py,sha256=vjtgJnZwnfESbFdca_Oeq9DtPkuQkSn7b-N3LsiDp78,308
2
2
  sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
3
3
  sourcecode/archetype.py,sha256=d7yoN6Tj4OBj-VgSMPEKg4WF9H8FypnZ5A6AkUh5oIE,34484
4
- sourcecode/architecture_analyzer.py,sha256=CoxlEDWn1axufjIT4fpNHd_2HNUKgqBClq9z9k6jp4M,51684
4
+ sourcecode/architecture_analyzer.py,sha256=1CEIeFtuxdzeFaeUtA5QuDmFkb5eilUK8zRmk9tcDxc,55644
5
5
  sourcecode/architecture_summary.py,sha256=pkl6lIOnSy-poa9EzW43U0MkXhx2FDfAFh8pDXLpcTo,26630
6
6
  sourcecode/ast_extractor.py,sha256=FEsYTsMCXbJfSYQZRk1k-I-PjUNGnKqO1IxMftsgA5U,51351
7
7
  sourcecode/cache.py,sha256=1V3vsaODAa2UBJAC0xpvxpmRdriCezQx5Q8JCcfgziE,31892
@@ -51,7 +51,7 @@ sourcecode/pr_impact.py,sha256=dCDVw83EDbyVf6F9ZmEQmsFz8ruVH7d4mpeKQCIZHM0,16805
51
51
  sourcecode/prepare_context.py,sha256=PYygAKL_jwz8gGElgM9mroUSih18UKSSjv08ndfl4aA,222877
52
52
  sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
53
53
  sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,12970
54
- sourcecode/reconciliation.py,sha256=kW8PyB_R7J2Ig0N_QP9Jdm1fQXM1Ebf3PZbaTc80ULA,7941
54
+ sourcecode/reconciliation.py,sha256=j3PctM_9FlaAV_OQ5_LjNhuZCehJ-XIwLo_iQbRQKTo,18335
55
55
  sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
56
56
  sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
57
57
  sourcecode/rename_refactor.py,sha256=h6dNFlB9aZ_3q6heeHBkgXQeXaT03nvPSsYH6P8qxFg,12965
@@ -75,7 +75,7 @@ sourcecode/spring_model.py,sha256=zOAgFmrRbG4a6KLm1TJl55aWMyPNsz3OS3FSczqPG6A,16
75
75
  sourcecode/spring_security_audit.py,sha256=Rk-aSohezdc7YDYbSoJquVnwpkDB8ty1BCD-4Hc4R5A,22832
76
76
  sourcecode/spring_semantic.py,sha256=jteQ1PkY9ArFJv0embg_jBIdbOxqrk9mQ2Xz8OF_FKA,14214
77
77
  sourcecode/spring_tx_analyzer.py,sha256=QMXkxa_hrqDVN3D8WJsd0nT7vfdZCT1E8ZWNoFFbvgU,34692
78
- sourcecode/summarizer.py,sha256=BaBO0WKQi4fX2b2Xjip-GLEZwI3PYiV_U9jmBQlrfFI,25060
78
+ sourcecode/summarizer.py,sha256=E70QILQ8cCNIH485c7lVMv5dXOpUn8qx1habi1_cn6o,26340
79
79
  sourcecode/tree_utils.py,sha256=8GAkIfQAsvtEudIeW1l4ooH_oRtrWR8cpJQJsEa_Pfw,2093
80
80
  sourcecode/type_usage_surface.py,sha256=51IrKRQoIoRnlsiDjHnqpJBn2rc6E59aRhgS0HTzAF0,4428
81
81
  sourcecode/validation_inference.py,sha256=UtmYv-d4TEzT1ihGEipAhtXDrwq0XtVtjMpb3i7NsNc,10815
@@ -137,8 +137,8 @@ sourcecode/telemetry/consent.py,sha256=LIAO9ohJZF8OuZwM4u1VWtALlYfTCCKq4wV3Vwc7i
137
137
  sourcecode/telemetry/events.py,sha256=LtzYfaX9Ilckj5PTvAcTpDa9mLqDsYPDUiDkRa58piY,2580
138
138
  sourcecode/telemetry/filters.py,sha256=NHa5T-6DaZduQPFuC34jOqHWQgSizM-Ygq8aZ4j19ng,5834
139
139
  sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
140
- sourcecode-2.5.4.dist-info/METADATA,sha256=vFyQq0a39iemrb04OqC6TwsnM_xA7j6PTBz_lbEccTM,10851
141
- sourcecode-2.5.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
142
- sourcecode-2.5.4.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
143
- sourcecode-2.5.4.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
144
- sourcecode-2.5.4.dist-info/RECORD,,
140
+ sourcecode-2.5.6.dist-info/METADATA,sha256=MW8dYaMUIriCSIBFCDunGYxcxAK0p_0WF1fQHY4jidE,10851
141
+ sourcecode-2.5.6.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
142
+ sourcecode-2.5.6.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
143
+ sourcecode-2.5.6.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
144
+ sourcecode-2.5.6.dist-info/RECORD,,