sourcecode 1.35.19__py3-none-any.whl → 1.35.22__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 +1 -1
- sourcecode/cli.py +117 -7
- sourcecode/dependency_analyzer.py +72 -0
- sourcecode/mcp/registry.py +52 -1
- sourcecode/mcp/server.py +168 -7
- sourcecode/repository_ir.py +7 -1
- sourcecode/spring_security_audit.py +14 -7
- sourcecode/spring_tx_analyzer.py +13 -6
- {sourcecode-1.35.19.dist-info → sourcecode-1.35.22.dist-info}/METADATA +3 -3
- {sourcecode-1.35.19.dist-info → sourcecode-1.35.22.dist-info}/RECORD +13 -13
- {sourcecode-1.35.19.dist-info → sourcecode-1.35.22.dist-info}/WHEEL +0 -0
- {sourcecode-1.35.19.dist-info → sourcecode-1.35.22.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.35.19.dist-info → sourcecode-1.35.22.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -3722,6 +3722,80 @@ def endpoints_cmd(
|
|
|
3722
3722
|
|
|
3723
3723
|
# ── Spring Semantic Audit ─────────────────────────────────────────────────────
|
|
3724
3724
|
|
|
3725
|
+
|
|
3726
|
+
def _render_spring_audit_github_comment(result: "SpringAuditResult", min_severity: str = "low") -> str: # type: ignore[name-defined]
|
|
3727
|
+
"""Render SpringAuditResult as a GitHub PR comment in Markdown."""
|
|
3728
|
+
from sourcecode.spring_findings import SEVERITY_ORDER
|
|
3729
|
+
|
|
3730
|
+
min_order = SEVERITY_ORDER.get(min_severity, 3)
|
|
3731
|
+
visible = [f for f in result.findings if SEVERITY_ORDER.get(f.severity, 3) <= min_order]
|
|
3732
|
+
|
|
3733
|
+
sev = result.summary.get("by_severity", {})
|
|
3734
|
+
total = result.summary.get("total_findings", 0)
|
|
3735
|
+
blocking = sev.get("critical", 0) + sev.get("high", 0)
|
|
3736
|
+
|
|
3737
|
+
_ICONS = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "🔵"}
|
|
3738
|
+
_LABELS = {"critical": "CRITICAL", "high": "HIGH", "medium": "MEDIUM", "low": "LOW"}
|
|
3739
|
+
|
|
3740
|
+
if total == 0:
|
|
3741
|
+
status_line = "✅ **Spring Audit — no findings**"
|
|
3742
|
+
elif blocking > 0:
|
|
3743
|
+
status_line = f"🔴 **Spring Audit — {total} finding{'s' if total != 1 else ''} ({blocking} blocking)**"
|
|
3744
|
+
else:
|
|
3745
|
+
status_line = f"🟡 **Spring Audit — {total} finding{'s' if total != 1 else ''} (0 blocking)**"
|
|
3746
|
+
|
|
3747
|
+
lines: list[str] = [status_line, ""]
|
|
3748
|
+
|
|
3749
|
+
if total > 0:
|
|
3750
|
+
severity_counts = []
|
|
3751
|
+
for sev_name in ("critical", "high", "medium", "low"):
|
|
3752
|
+
n = sev.get(sev_name, 0)
|
|
3753
|
+
if n:
|
|
3754
|
+
severity_counts.append(f"{_ICONS[sev_name]} {n} {sev_name}")
|
|
3755
|
+
lines.append("**Severity:** " + " · ".join(severity_counts))
|
|
3756
|
+
lines.append("")
|
|
3757
|
+
|
|
3758
|
+
if not visible:
|
|
3759
|
+
lines.append(f"_No findings at or above `{min_severity}` severity._")
|
|
3760
|
+
return "\n".join(lines)
|
|
3761
|
+
|
|
3762
|
+
lines += [
|
|
3763
|
+
"| Sev | Pattern | File | Symbol | Title |",
|
|
3764
|
+
"|-----|---------|------|--------|-------|",
|
|
3765
|
+
]
|
|
3766
|
+
for f in sorted(visible, key=lambda x: (SEVERITY_ORDER.get(x.severity, 3), x.source_file)):
|
|
3767
|
+
icon = _ICONS.get(f.severity, "")
|
|
3768
|
+
label = _LABELS.get(f.severity, f.severity.upper())
|
|
3769
|
+
short_file = f.source_file.split("/")[-1] if "/" in f.source_file else f.source_file
|
|
3770
|
+
short_sym = f.symbol.split(".")[-1] if "." in f.symbol else f.symbol
|
|
3771
|
+
title_escaped = f.title.replace("|", "\\|")
|
|
3772
|
+
lines.append(f"| {icon} {label} | `{f.pattern_id}` | `{short_file}` | `{short_sym}` | {title_escaped} |")
|
|
3773
|
+
|
|
3774
|
+
lines.append("")
|
|
3775
|
+
|
|
3776
|
+
if visible:
|
|
3777
|
+
lines.append("<details>")
|
|
3778
|
+
lines.append("<summary>Finding details</summary>")
|
|
3779
|
+
lines.append("")
|
|
3780
|
+
for f in sorted(visible, key=lambda x: (SEVERITY_ORDER.get(x.severity, 3), x.source_file)):
|
|
3781
|
+
icon = _ICONS.get(f.severity, "")
|
|
3782
|
+
lines.append(f"### {icon} `{f.pattern_id}` — {f.title}")
|
|
3783
|
+
lines.append(f"**File:** `{f.source_file}` **Symbol:** `{f.symbol}`")
|
|
3784
|
+
lines.append("")
|
|
3785
|
+
lines.append(f.explanation)
|
|
3786
|
+
lines.append("")
|
|
3787
|
+
lines.append(f"**Fix:** {f.fix_hint}")
|
|
3788
|
+
lines.append("")
|
|
3789
|
+
lines.append("</details>")
|
|
3790
|
+
|
|
3791
|
+
lines += [
|
|
3792
|
+
"",
|
|
3793
|
+
f"_Generated by [sourcecode](https://github.com/sourcecode-ai/sourcecode) · "
|
|
3794
|
+
f"scope: {result.scope} · min-severity: {min_severity}_",
|
|
3795
|
+
]
|
|
3796
|
+
return "\n".join(lines)
|
|
3797
|
+
|
|
3798
|
+
|
|
3725
3799
|
@app.command("spring-audit")
|
|
3726
3800
|
def spring_audit_cmd(
|
|
3727
3801
|
path: Path = typer.Argument(
|
|
@@ -3736,7 +3810,7 @@ def spring_audit_cmd(
|
|
|
3736
3810
|
"json",
|
|
3737
3811
|
"--format",
|
|
3738
3812
|
"-f",
|
|
3739
|
-
help="Output format: json (default) or
|
|
3813
|
+
help="Output format: json (default), yaml, or github-comment.",
|
|
3740
3814
|
show_default=True,
|
|
3741
3815
|
),
|
|
3742
3816
|
copy: bool = typer.Option(
|
|
@@ -3758,6 +3832,11 @@ def spring_audit_cmd(
|
|
|
3758
3832
|
help="Minimum severity to include: critical, high, medium, or low (default).",
|
|
3759
3833
|
show_default=True,
|
|
3760
3834
|
),
|
|
3835
|
+
ci: bool = typer.Option(
|
|
3836
|
+
False,
|
|
3837
|
+
"--ci/--no-ci",
|
|
3838
|
+
help="Exit with code 1 if any findings at or above --min-severity are found. For CI/CD gates.",
|
|
3839
|
+
),
|
|
3761
3840
|
) -> None:
|
|
3762
3841
|
"""Spring semantic audit: TX anomalies (TX-001..005) + security surface (SEC-001..003).
|
|
3763
3842
|
|
|
@@ -3772,6 +3851,12 @@ def spring_audit_cmd(
|
|
|
3772
3851
|
SEC-002 CVE-2025-41248: @PreAuthorize on inherited method from generic supertype
|
|
3773
3852
|
SEC-003 @Transactional on @Controller/@RestController (TX in wrong layer)
|
|
3774
3853
|
|
|
3854
|
+
\b
|
|
3855
|
+
CI/CD usage:
|
|
3856
|
+
sourcecode spring-audit . --ci # exit 1 on any finding
|
|
3857
|
+
sourcecode spring-audit . --ci --min-severity high # exit 1 only on high/critical
|
|
3858
|
+
sourcecode spring-audit . --ci --format github-comment # Markdown PR comment + exit 1
|
|
3859
|
+
|
|
3775
3860
|
\b
|
|
3776
3861
|
Examples:
|
|
3777
3862
|
sourcecode spring-audit .
|
|
@@ -3818,15 +3903,28 @@ def spring_audit_cmd(
|
|
|
3818
3903
|
)
|
|
3819
3904
|
raise typer.Exit(code=1)
|
|
3820
3905
|
|
|
3821
|
-
|
|
3906
|
+
if format not in ("json", "yaml", "github-comment"):
|
|
3907
|
+
_emit_error_json(
|
|
3908
|
+
INVALID_INPUT_CODE,
|
|
3909
|
+
f"Invalid format '{format}'.",
|
|
3910
|
+
hint="format must be one of: json, yaml, github-comment.",
|
|
3911
|
+
expected="json | yaml | github-comment",
|
|
3912
|
+
)
|
|
3913
|
+
raise typer.Exit(code=1)
|
|
3914
|
+
|
|
3915
|
+
_file_limitations: list[str] = []
|
|
3916
|
+
file_list = find_java_files(target, limitations=_file_limitations)
|
|
3822
3917
|
if not file_list:
|
|
3823
|
-
|
|
3918
|
+
empty_result = SpringAuditResult(
|
|
3824
3919
|
spring_detected=False,
|
|
3825
3920
|
scope=scope,
|
|
3826
3921
|
limitations=["No Java files found in repository — Spring audit requires Java source."],
|
|
3827
3922
|
metadata={"java_files_found": 0},
|
|
3828
|
-
).finalize()
|
|
3829
|
-
|
|
3923
|
+
).finalize()
|
|
3924
|
+
if format == "github-comment":
|
|
3925
|
+
output = _render_spring_audit_github_comment(empty_result, min_severity)
|
|
3926
|
+
else:
|
|
3927
|
+
output = _serialize_dict(empty_result.to_dict(), format)
|
|
3830
3928
|
if output_path is not None:
|
|
3831
3929
|
output_path.write_text(output, encoding="utf-8")
|
|
3832
3930
|
typer.echo("Spring audit written to " + str(output_path), err=True)
|
|
@@ -3864,6 +3962,9 @@ def spring_audit_cmd(
|
|
|
3864
3962
|
metadata=merged_meta,
|
|
3865
3963
|
).finalize()
|
|
3866
3964
|
|
|
3965
|
+
if _file_limitations:
|
|
3966
|
+
combined.limitations.extend(_file_limitations)
|
|
3967
|
+
|
|
3867
3968
|
# Populate git_head from repo HEAD — non-fatal.
|
|
3868
3969
|
try:
|
|
3869
3970
|
import subprocess as _sub_sa
|
|
@@ -3885,7 +3986,10 @@ def spring_audit_cmd(
|
|
|
3885
3986
|
except Exception:
|
|
3886
3987
|
pass
|
|
3887
3988
|
|
|
3888
|
-
|
|
3989
|
+
if format == "github-comment":
|
|
3990
|
+
output = _render_spring_audit_github_comment(combined, min_severity)
|
|
3991
|
+
else:
|
|
3992
|
+
output = _serialize_dict(data, format)
|
|
3889
3993
|
|
|
3890
3994
|
if output_path is not None:
|
|
3891
3995
|
output_path.write_text(output, encoding="utf-8")
|
|
@@ -3899,6 +4003,9 @@ def spring_audit_cmd(
|
|
|
3899
4003
|
if _copy_to_clipboard(output):
|
|
3900
4004
|
typer.echo("✓ copied to clipboard", err=True)
|
|
3901
4005
|
|
|
4006
|
+
if ci and combined.findings:
|
|
4007
|
+
raise typer.Exit(code=1)
|
|
4008
|
+
|
|
3902
4009
|
|
|
3903
4010
|
# ── Spring Boot Migration Check ───────────────────────────────────────────────
|
|
3904
4011
|
|
|
@@ -3985,8 +4092,11 @@ def migrate_check_cmd(
|
|
|
3985
4092
|
)
|
|
3986
4093
|
raise typer.Exit(code=1)
|
|
3987
4094
|
|
|
3988
|
-
|
|
4095
|
+
_file_limitations: list[str] = []
|
|
4096
|
+
file_list = find_java_files(target, limitations=_file_limitations)
|
|
3989
4097
|
report = run_migrate_check(file_list, target, min_severity=min_severity)
|
|
4098
|
+
if _file_limitations:
|
|
4099
|
+
report.limitations.extend(_file_limitations)
|
|
3990
4100
|
|
|
3991
4101
|
if format == "text":
|
|
3992
4102
|
output = report.to_text(min_severity=min_severity)
|
|
@@ -1142,6 +1142,78 @@ class DependencyAnalyzer:
|
|
|
1142
1142
|
records: list[DependencyRecord] = []
|
|
1143
1143
|
deps_elem = root_elem.find(f"{ns}dependencies")
|
|
1144
1144
|
if deps_elem is None:
|
|
1145
|
+
# Multi-module aggregator POM: scan up to 5 declared submodule pom.xml files.
|
|
1146
|
+
modules_elem = root_elem.find(f"{ns}modules")
|
|
1147
|
+
if modules_elem is not None:
|
|
1148
|
+
submodule_records: list[DependencyRecord] = []
|
|
1149
|
+
submodule_limitations: list[str] = []
|
|
1150
|
+
seen_submodule_deps: set[str] = set()
|
|
1151
|
+
_all_modules = list(modules_elem.findall(f"{ns}module"))
|
|
1152
|
+
if len(_all_modules) > 5:
|
|
1153
|
+
submodule_limitations.append(
|
|
1154
|
+
f"MAX_SUBMODULES_REACHED: scanned 5 of {len(_all_modules)} declared Maven submodules"
|
|
1155
|
+
)
|
|
1156
|
+
for mod_elem in _all_modules[:5]:
|
|
1157
|
+
mod_name = (mod_elem.text or "").strip()
|
|
1158
|
+
if not mod_name:
|
|
1159
|
+
continue
|
|
1160
|
+
sub_pom = root / mod_name / "pom.xml"
|
|
1161
|
+
if not sub_pom.exists():
|
|
1162
|
+
continue
|
|
1163
|
+
try:
|
|
1164
|
+
sub_tree = ET.parse(sub_pom)
|
|
1165
|
+
except (ET.ParseError, OSError):
|
|
1166
|
+
continue
|
|
1167
|
+
sub_root = sub_tree.getroot()
|
|
1168
|
+
sub_ns_match = re.match(r"\{[^}]+\}", sub_root.tag)
|
|
1169
|
+
sub_ns = sub_ns_match.group(0) if sub_ns_match else ""
|
|
1170
|
+
sub_props = self._parse_maven_properties(sub_root, sub_ns)
|
|
1171
|
+
sub_dm = self._parse_dependency_management(sub_root, sub_ns, sub_props)
|
|
1172
|
+
# Inherit parent properties for version resolution
|
|
1173
|
+
for k, v in properties.items():
|
|
1174
|
+
sub_props.setdefault(k, v)
|
|
1175
|
+
for k, v in dm_versions.items():
|
|
1176
|
+
sub_dm.setdefault(k, v)
|
|
1177
|
+
sub_deps_elem = sub_root.find(f"{sub_ns}dependencies")
|
|
1178
|
+
if sub_deps_elem is None:
|
|
1179
|
+
continue
|
|
1180
|
+
for dep in sub_deps_elem.findall(f"{sub_ns}dependency"):
|
|
1181
|
+
gid = (dep.findtext(f"{sub_ns}groupId") or "").strip()
|
|
1182
|
+
aid = (dep.findtext(f"{sub_ns}artifactId") or "").strip()
|
|
1183
|
+
if not gid or not aid:
|
|
1184
|
+
continue
|
|
1185
|
+
dep_key = f"{gid}:{aid}"
|
|
1186
|
+
if dep_key in seen_submodule_deps:
|
|
1187
|
+
continue
|
|
1188
|
+
seen_submodule_deps.add(dep_key)
|
|
1189
|
+
ver_raw = (dep.findtext(f"{sub_ns}version") or "").strip() or None
|
|
1190
|
+
declared = self._resolve_maven_version(ver_raw, sub_props)
|
|
1191
|
+
if declared is None:
|
|
1192
|
+
declared = sub_dm.get(dep_key)
|
|
1193
|
+
scope_text = (dep.findtext(f"{sub_ns}scope") or "compile").strip().lower()
|
|
1194
|
+
if scope_text == "test":
|
|
1195
|
+
scope = "dev"
|
|
1196
|
+
elif scope_text == "provided":
|
|
1197
|
+
scope = "provided"
|
|
1198
|
+
else:
|
|
1199
|
+
scope = "direct"
|
|
1200
|
+
resolved_version = None
|
|
1201
|
+
if declared is None and parent_version:
|
|
1202
|
+
if gid == "org.springframework.boot":
|
|
1203
|
+
resolved_version = parent_version
|
|
1204
|
+
elif gid == "org.springframework.security" and "spring-security.version" in sub_props:
|
|
1205
|
+
resolved_version = sub_props["spring-security.version"]
|
|
1206
|
+
submodule_records.append(DependencyRecord(
|
|
1207
|
+
name=dep_key,
|
|
1208
|
+
ecosystem="java",
|
|
1209
|
+
scope=scope,
|
|
1210
|
+
declared_version=declared,
|
|
1211
|
+
resolved_version=resolved_version,
|
|
1212
|
+
source="manifest",
|
|
1213
|
+
manifest_path=f"{mod_name}/pom.xml",
|
|
1214
|
+
))
|
|
1215
|
+
if submodule_records:
|
|
1216
|
+
return submodule_records, submodule_limitations
|
|
1145
1217
|
return [], ["java: pom.xml has no <dependencies> block"]
|
|
1146
1218
|
|
|
1147
1219
|
for dep in deps_elem.findall(f"{ns}dependency"):
|
sourcecode/mcp/registry.py
CHANGED
|
@@ -1221,6 +1221,7 @@ _MCP_HIDDEN_CANONICAL_TOOLS: frozenset[str] = frozenset({
|
|
|
1221
1221
|
# Listed here so validate_registry() skips CLI param-drift checks on the alias.
|
|
1222
1222
|
"spring_audit", # curated: repo_path + scope + min_severity only (strips output_path/format/copy)
|
|
1223
1223
|
"impact_chain", # curated: repo_path + symbol + depth + query_type with choices
|
|
1224
|
+
"migrate_check", # curated: repo_path + min_severity only (strips output_path/format/copy/ci)
|
|
1224
1225
|
# MCP self-management (an agent is not the MCP client admin)
|
|
1225
1226
|
"mcp_init",
|
|
1226
1227
|
"mcp_serve",
|
|
@@ -1349,7 +1350,57 @@ query_type: "impact" (default) | "events"
|
|
|
1349
1350
|
docstring_override=_IMPACT_CHAIN_DOC,
|
|
1350
1351
|
)
|
|
1351
1352
|
|
|
1352
|
-
|
|
1353
|
+
_MIGRATE_CHECK_DOC = """\
|
|
1354
|
+
Spring Boot 2→3 migration readiness: javax→jakarta namespace blockers. JAVA ONLY.
|
|
1355
|
+
|
|
1356
|
+
When to call: when asked about Spring Boot migration readiness, javax vs jakarta imports,
|
|
1357
|
+
or upgrading from Spring Boot 2.x to 3.x. Use BEFORE get_spring_audit when the goal
|
|
1358
|
+
is migration planning rather than ongoing Spring semantic audit.
|
|
1359
|
+
Do NOT call on non-Java repositories — returns readiness_score=100 with no findings.
|
|
1360
|
+
|
|
1361
|
+
Rules detected:
|
|
1362
|
+
MIG-001 critical — javax.persistence imports (JPA; will not compile after migration)
|
|
1363
|
+
MIG-002 high — javax.servlet imports (Servlet API changed)
|
|
1364
|
+
MIG-003 high — javax.validation imports (Bean Validation changed)
|
|
1365
|
+
MIG-004 high — javax.transaction imports (TX API changed)
|
|
1366
|
+
MIG-005 high — extends WebSecurityConfigurerAdapter (removed in Spring Security 6)
|
|
1367
|
+
MIG-006 medium — javax.annotation imports (CDI annotations)
|
|
1368
|
+
MIG-007 medium — javax.inject imports (DI annotations)
|
|
1369
|
+
MIG-008 medium — javax.ws.rs imports (JAX-RS API)
|
|
1370
|
+
|
|
1371
|
+
Returns: schema_version, readiness_score (0–100; 100=ready to migrate), blocking_count,
|
|
1372
|
+
estimated_effort_days, spring_boot_2_detected, summary (total_findings, affected_files,
|
|
1373
|
+
by_severity, by_rule), findings[], limitations, metadata.
|
|
1374
|
+
findings fields: id, rule_id, severity, title, source_file, first_line,
|
|
1375
|
+
imports_found, explanation, fix_hint.
|
|
1376
|
+
|
|
1377
|
+
repo_path: absolute path to the Java repository (default: current working directory).
|
|
1378
|
+
min_severity: "low" (default) | "medium" | "high" | "critical" — filter threshold.
|
|
1379
|
+
"""
|
|
1380
|
+
|
|
1381
|
+
migrate_check = _alias_spec(
|
|
1382
|
+
"migrate_check",
|
|
1383
|
+
"Spring Boot 2→3 migration readiness: javax→jakarta blockers. JAVA ONLY.",
|
|
1384
|
+
("migrate-check",),
|
|
1385
|
+
(
|
|
1386
|
+
ToolParamSpec("repo_path", "argument", str, required=False, default=".", is_path=True,
|
|
1387
|
+
help="Absolute path to the Java repository."),
|
|
1388
|
+
ToolParamSpec("min_severity", "option", str, required=False, default="low",
|
|
1389
|
+
option_names=("--min-severity",), choices=("low", "medium", "high", "critical"),
|
|
1390
|
+
help="low (default) | medium | high | critical"),
|
|
1391
|
+
),
|
|
1392
|
+
lambda inputs: [
|
|
1393
|
+
"migrate-check",
|
|
1394
|
+
str(inputs.get("repo_path", ".")),
|
|
1395
|
+
"--min-severity", str(inputs.get("min_severity", "low")),
|
|
1396
|
+
],
|
|
1397
|
+
supported_targets=("repo_path",),
|
|
1398
|
+
unsupported_targets=("file_path",),
|
|
1399
|
+
validator=validate_repo_path,
|
|
1400
|
+
docstring_override=_MIGRATE_CHECK_DOC,
|
|
1401
|
+
)
|
|
1402
|
+
|
|
1403
|
+
return [spring_audit, impact_chain, migrate_check]
|
|
1353
1404
|
|
|
1354
1405
|
|
|
1355
1406
|
@lru_cache(maxsize=1)
|
sourcecode/mcp/server.py
CHANGED
|
@@ -192,7 +192,48 @@ def _execute(args: list[str]) -> dict | CallToolResult:
|
|
|
192
192
|
return _ok(result)
|
|
193
193
|
|
|
194
194
|
|
|
195
|
+
# Per-tool character budgets (4 chars ≈ 1 token; keeps Cursor under its 10k token limit).
|
|
196
|
+
# List fields are trimmed front-to-back until output fits.
|
|
197
|
+
_MCP_CHAR_BUDGETS: dict[str, int] = {
|
|
198
|
+
"get_agent_context": 38_000,
|
|
199
|
+
"get_spring_audit": 38_000,
|
|
200
|
+
"get_migration_readiness": 38_000,
|
|
201
|
+
}
|
|
202
|
+
_MCP_TRIM_FIELDS = (
|
|
203
|
+
"findings", "relevant_files", "key_dependencies",
|
|
204
|
+
"entry_points", "limitations", "gaps", "code_notes",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _cap_mcp_output(tool_name: str, data: Any) -> Any:
|
|
209
|
+
"""Trim list fields in data until JSON serialisation fits within the tool's char budget."""
|
|
210
|
+
budget = _MCP_CHAR_BUDGETS.get(tool_name)
|
|
211
|
+
if not budget or not isinstance(data, dict):
|
|
212
|
+
return data
|
|
213
|
+
if len(json.dumps(data, default=str)) <= budget:
|
|
214
|
+
return data
|
|
215
|
+
result = dict(data)
|
|
216
|
+
for field in _MCP_TRIM_FIELDS:
|
|
217
|
+
if field not in result or not isinstance(result[field], list):
|
|
218
|
+
continue
|
|
219
|
+
items = list(result[field])
|
|
220
|
+
while items and len(json.dumps(result, default=str)) > budget:
|
|
221
|
+
items = items[:-1]
|
|
222
|
+
result[field] = items
|
|
223
|
+
if len(json.dumps(result, default=str)) <= budget:
|
|
224
|
+
break
|
|
225
|
+
if len(json.dumps(result, default=str)) > budget:
|
|
226
|
+
result["_mcp_truncated"] = True
|
|
227
|
+
result["_mcp_truncation_note"] = (
|
|
228
|
+
f"Output capped at ~{budget // 4}k tokens for MCP compatibility. "
|
|
229
|
+
"Use CLI directly for full output."
|
|
230
|
+
)
|
|
231
|
+
return result
|
|
232
|
+
|
|
233
|
+
|
|
195
234
|
_DEFAULT_TESTS_TIMEOUT_MS = 15_000
|
|
235
|
+
_DEFAULT_SPRING_AUDIT_TIMEOUT_MS = 120_000 # 2 min
|
|
236
|
+
_DEFAULT_IMPACT_TIMEOUT_MS = 60_000 # 1 min
|
|
196
237
|
|
|
197
238
|
# Regex for MINGW paths: /c/some/path → C:/some/path
|
|
198
239
|
_MINGW_PATH_RE = re.compile(r"^/([a-zA-Z])(/.*)?$")
|
|
@@ -568,7 +609,10 @@ def get_agent_context(repo_path: str = ".", git_context: bool = False) -> dict:
|
|
|
568
609
|
args = [repo_path, "--agent"]
|
|
569
610
|
if git_context:
|
|
570
611
|
args.append("--git-context")
|
|
571
|
-
|
|
612
|
+
result = _execute(args)
|
|
613
|
+
if isinstance(result, dict) and result.get("success"):
|
|
614
|
+
result = _ok(_cap_mcp_output("get_agent_context", result.get("data")))
|
|
615
|
+
return result
|
|
572
616
|
except Exception as exc:
|
|
573
617
|
return _err(
|
|
574
618
|
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
@@ -641,7 +685,79 @@ def get_spring_audit(repo_path: str = ".", scope: str = "all") -> dict:
|
|
|
641
685
|
_path_err = _check_repo_path(repo_path)
|
|
642
686
|
if _path_err is not None:
|
|
643
687
|
return _path_err
|
|
644
|
-
|
|
688
|
+
timeout_ms = int(os.environ.get("SOURCECODE_SPRING_AUDIT_TIMEOUT_MS", str(_DEFAULT_SPRING_AUDIT_TIMEOUT_MS)))
|
|
689
|
+
timeout_s = timeout_ms / 1000.0
|
|
690
|
+
_exec = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
|
691
|
+
try:
|
|
692
|
+
_fut = _exec.submit(_execute, ["spring-audit", repo_path, "--scope", scope])
|
|
693
|
+
_done, _pending = concurrent.futures.wait([_fut], timeout=timeout_s)
|
|
694
|
+
if _pending:
|
|
695
|
+
_exec.shutdown(wait=False)
|
|
696
|
+
return _ok({
|
|
697
|
+
"truncated": True,
|
|
698
|
+
"truncated_reason": f"timeout_{timeout_s:.0f}s",
|
|
699
|
+
"analysis": "timed out — repository may be too large for MCP transport",
|
|
700
|
+
"suggestion": f"Increase SOURCECODE_SPRING_AUDIT_TIMEOUT_MS (current: {timeout_ms}ms) or run via CLI directly",
|
|
701
|
+
})
|
|
702
|
+
result = _fut.result()
|
|
703
|
+
finally:
|
|
704
|
+
_exec.shutdown(wait=True)
|
|
705
|
+
if isinstance(result, dict) and result.get("success"):
|
|
706
|
+
result = _ok(_cap_mcp_output("get_spring_audit", result.get("data")))
|
|
707
|
+
return result
|
|
708
|
+
except Exception as exc:
|
|
709
|
+
return _err(
|
|
710
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
711
|
+
"INTERNAL_ERROR",
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
@mcp.tool()
|
|
716
|
+
def get_migration_readiness(repo_path: str = ".", min_severity: str = "low") -> dict:
|
|
717
|
+
"""Spring Boot 2→3 migration readiness: javax→jakarta namespace blockers. JAVA ONLY.
|
|
718
|
+
|
|
719
|
+
When to call: when asked about Spring Boot migration readiness, javax vs jakarta imports,
|
|
720
|
+
or upgrading from Spring Boot 2.x to 3.x. Call this BEFORE get_spring_audit when
|
|
721
|
+
the goal is migration planning — not ongoing audit.
|
|
722
|
+
Do NOT call on non-Java repositories — returns readiness_score=100 with no findings.
|
|
723
|
+
|
|
724
|
+
Maps to: sourcecode migrate-check <repo_path> --min-severity <min_severity>
|
|
725
|
+
Returns: MigrationReport with schema_version, readiness_score (0–100; 100=ready to migrate),
|
|
726
|
+
blocking_count, estimated_effort_days, spring_boot_2_detected,
|
|
727
|
+
summary (total_findings, affected_files, by_severity, by_rule),
|
|
728
|
+
findings[], limitations, metadata.
|
|
729
|
+
findings fields: id, rule_id, severity, title, source_file, first_line,
|
|
730
|
+
imports_found, explanation, fix_hint.
|
|
731
|
+
Rules:
|
|
732
|
+
MIG-001 critical — javax.persistence (JPA, will not compile after migration)
|
|
733
|
+
MIG-002 high — javax.servlet (Servlet API)
|
|
734
|
+
MIG-003 high — javax.validation (Bean Validation)
|
|
735
|
+
MIG-004 high — javax.transaction (TX API)
|
|
736
|
+
MIG-005 high — extends WebSecurityConfigurerAdapter (removed in Spring Security 6)
|
|
737
|
+
MIG-006 medium — javax.annotation (CDI annotations)
|
|
738
|
+
MIG-007 medium — javax.inject (DI annotations)
|
|
739
|
+
MIG-008 medium — javax.ws.rs (JAX-RS API)
|
|
740
|
+
|
|
741
|
+
repo_path: absolute path to the Java repository (default: current working directory).
|
|
742
|
+
min_severity: "low" (default) | "medium" | "high" | "critical" — filter threshold.
|
|
743
|
+
"""
|
|
744
|
+
_raw = repo_path
|
|
745
|
+
try:
|
|
746
|
+
if not isinstance(repo_path, str):
|
|
747
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
748
|
+
if min_severity not in ("critical", "high", "medium", "low"):
|
|
749
|
+
return _err(
|
|
750
|
+
f"Invalid min_severity '{min_severity}' — must be one of: critical, high, medium, low",
|
|
751
|
+
"INVALID_ARGUMENT",
|
|
752
|
+
)
|
|
753
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
754
|
+
_path_err = _check_repo_path(repo_path)
|
|
755
|
+
if _path_err is not None:
|
|
756
|
+
return _path_err
|
|
757
|
+
result = _execute(["migrate-check", repo_path, "--min-severity", min_severity])
|
|
758
|
+
if isinstance(result, dict) and result.get("success"):
|
|
759
|
+
result = _ok(_cap_mcp_output("get_migration_readiness", result.get("data")))
|
|
760
|
+
return result
|
|
645
761
|
except Exception as exc:
|
|
646
762
|
return _err(
|
|
647
763
|
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
@@ -681,7 +797,23 @@ def get_impact_chain(repo_path: str = ".", symbol: str = "", depth: int = 4) ->
|
|
|
681
797
|
if _path_err is not None:
|
|
682
798
|
return _path_err
|
|
683
799
|
args = ["impact-chain", symbol.strip(), repo_path, "--depth", str(depth)]
|
|
684
|
-
|
|
800
|
+
timeout_ms = int(os.environ.get("SOURCECODE_IMPACT_TIMEOUT_MS", str(_DEFAULT_IMPACT_TIMEOUT_MS)))
|
|
801
|
+
timeout_s = timeout_ms / 1000.0
|
|
802
|
+
_exec = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
|
803
|
+
try:
|
|
804
|
+
_fut = _exec.submit(_execute, args)
|
|
805
|
+
_done, _pending = concurrent.futures.wait([_fut], timeout=timeout_s)
|
|
806
|
+
if _pending:
|
|
807
|
+
_exec.shutdown(wait=False)
|
|
808
|
+
return _ok({
|
|
809
|
+
"truncated": True,
|
|
810
|
+
"truncated_reason": f"timeout_{timeout_s:.0f}s",
|
|
811
|
+
"analysis": "timed out — repository may be too large for MCP transport",
|
|
812
|
+
"suggestion": f"Increase SOURCECODE_IMPACT_TIMEOUT_MS (current: {timeout_ms}ms) or run via CLI directly",
|
|
813
|
+
})
|
|
814
|
+
return _fut.result()
|
|
815
|
+
finally:
|
|
816
|
+
_exec.shutdown(wait=True)
|
|
685
817
|
except Exception as exc:
|
|
686
818
|
return _err(
|
|
687
819
|
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
@@ -1098,7 +1230,23 @@ def get_impact_context(repo_path: str = ".", target: str = "", depth: int = 4) -
|
|
|
1098
1230
|
if _path_err is not None:
|
|
1099
1231
|
return _path_err
|
|
1100
1232
|
args = ["impact", target.strip(), repo_path, "--depth", str(depth)]
|
|
1101
|
-
|
|
1233
|
+
timeout_ms = int(os.environ.get("SOURCECODE_IMPACT_TIMEOUT_MS", str(_DEFAULT_IMPACT_TIMEOUT_MS)))
|
|
1234
|
+
timeout_s = timeout_ms / 1000.0
|
|
1235
|
+
_exec = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
|
1236
|
+
try:
|
|
1237
|
+
_fut = _exec.submit(_execute, args)
|
|
1238
|
+
_done, _pending = concurrent.futures.wait([_fut], timeout=timeout_s)
|
|
1239
|
+
if _pending:
|
|
1240
|
+
_exec.shutdown(wait=False)
|
|
1241
|
+
return _ok({
|
|
1242
|
+
"truncated": True,
|
|
1243
|
+
"truncated_reason": f"timeout_{timeout_s:.0f}s",
|
|
1244
|
+
"analysis": "timed out — repository may be too large for MCP transport",
|
|
1245
|
+
"suggestion": f"Increase SOURCECODE_IMPACT_TIMEOUT_MS (current: {timeout_ms}ms) or run via CLI directly",
|
|
1246
|
+
})
|
|
1247
|
+
return _fut.result()
|
|
1248
|
+
finally:
|
|
1249
|
+
_exec.shutdown(wait=True)
|
|
1102
1250
|
except Exception as exc:
|
|
1103
1251
|
return _err(
|
|
1104
1252
|
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
@@ -1219,9 +1367,22 @@ def _finalize_mcp_registry() -> None:
|
|
|
1219
1367
|
structured_output=False,
|
|
1220
1368
|
)
|
|
1221
1369
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1370
|
+
try:
|
|
1371
|
+
drift = validate_registry()
|
|
1372
|
+
if drift:
|
|
1373
|
+
import warnings
|
|
1374
|
+
warnings.warn(
|
|
1375
|
+
f"MCP registry drift detected — server running with potential tool mismatch: {drift}",
|
|
1376
|
+
RuntimeWarning,
|
|
1377
|
+
stacklevel=2,
|
|
1378
|
+
)
|
|
1379
|
+
except Exception as _reg_exc:
|
|
1380
|
+
import warnings
|
|
1381
|
+
warnings.warn(
|
|
1382
|
+
f"MCP registry validation failed — server starting in degraded mode: {_reg_exc}",
|
|
1383
|
+
RuntimeWarning,
|
|
1384
|
+
stacklevel=2,
|
|
1385
|
+
)
|
|
1225
1386
|
|
|
1226
1387
|
|
|
1227
1388
|
_finalize_mcp_registry()
|
sourcecode/repository_ir.py
CHANGED
|
@@ -3312,11 +3312,13 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
|
|
|
3312
3312
|
}
|
|
3313
3313
|
|
|
3314
3314
|
|
|
3315
|
-
def find_java_files(root: Path, *, max_files: int = 8000) -> list[str]:
|
|
3315
|
+
def find_java_files(root: Path, *, max_files: int = 8000, limitations: list[str] | None = None) -> list[str]:
|
|
3316
3316
|
"""Return relative paths to Java files under root, excluding test dirs and vendor."""
|
|
3317
3317
|
results: list[str] = []
|
|
3318
|
+
_capped = False
|
|
3318
3319
|
for p in sorted(root.rglob("*.java")):
|
|
3319
3320
|
if len(results) >= max_files:
|
|
3321
|
+
_capped = True
|
|
3320
3322
|
break
|
|
3321
3323
|
try:
|
|
3322
3324
|
rel = str(p.relative_to(root)).replace("\\", "/")
|
|
@@ -3334,6 +3336,10 @@ def find_java_files(root: Path, *, max_files: int = 8000) -> list[str]:
|
|
|
3334
3336
|
if any(f in rel for f in ("/admin-client/", "/rest-client/", "/client-api/", "/api-client/")):
|
|
3335
3337
|
continue
|
|
3336
3338
|
results.append(rel)
|
|
3339
|
+
if _capped and limitations is not None:
|
|
3340
|
+
limitations.append(
|
|
3341
|
+
f"MAX_JAVA_FILES_REACHED: scanned {max_files} files — repository likely has more"
|
|
3342
|
+
)
|
|
3337
3343
|
return results
|
|
3338
3344
|
|
|
3339
3345
|
|
|
@@ -418,12 +418,15 @@ class SecurityScanner:
|
|
|
418
418
|
model: Optional[SpringSemanticModel] = None,
|
|
419
419
|
) -> list[SpringFinding]:
|
|
420
420
|
all_findings: list[SpringFinding] = []
|
|
421
|
+
self._last_analysis_errors: list[str] = []
|
|
421
422
|
for pattern in self.patterns:
|
|
422
423
|
try:
|
|
423
424
|
found = _call_pattern_analyze(pattern, cir, tx_index, root, model)
|
|
424
425
|
all_findings.extend(found)
|
|
425
|
-
except Exception:
|
|
426
|
-
|
|
426
|
+
except Exception as exc:
|
|
427
|
+
self._last_analysis_errors.append(
|
|
428
|
+
f"{pattern.pattern_id}: {type(exc).__name__}: {exc}"
|
|
429
|
+
)
|
|
427
430
|
deduped = deduplicate_findings(all_findings)
|
|
428
431
|
return sorted(deduped, key=lambda f: (SEVERITY_ORDER.get(f.severity, 9), f.symbol))
|
|
429
432
|
|
|
@@ -475,16 +478,20 @@ def run_security_audit(
|
|
|
475
478
|
or cir.metadata.get("security_model", "unknown") != "unknown"
|
|
476
479
|
)
|
|
477
480
|
|
|
481
|
+
_sec_limitations = [
|
|
482
|
+
"SEC-001: only emitted for annotation_based security model",
|
|
483
|
+
"SEC-002: generic type detection is regex-based on extends edge signatures",
|
|
484
|
+
"SEC-003: only detects controllers visible via cir.endpoints",
|
|
485
|
+
]
|
|
486
|
+
for _err in getattr(scanner, "_last_analysis_errors", []):
|
|
487
|
+
_sec_limitations.append(f"PATTERN_ERROR: {_err}")
|
|
488
|
+
|
|
478
489
|
result = SpringAuditResult(
|
|
479
490
|
repo_id=getattr(cir, "cir_hash", "")[:16],
|
|
480
491
|
spring_detected=_spring_detected,
|
|
481
492
|
scope="security",
|
|
482
493
|
findings=findings,
|
|
483
|
-
limitations=
|
|
484
|
-
"SEC-001: only emitted for annotation_based security model",
|
|
485
|
-
"SEC-002: generic type detection is regex-based on extends edge signatures",
|
|
486
|
-
"SEC-003: only detects controllers visible via cir.endpoints",
|
|
487
|
-
],
|
|
494
|
+
limitations=_sec_limitations,
|
|
488
495
|
metadata={
|
|
489
496
|
"endpoints_analyzed": len(cir.endpoints),
|
|
490
497
|
"security_model": cir.metadata.get("security_model", "unknown"),
|
sourcecode/spring_tx_analyzer.py
CHANGED
|
@@ -672,12 +672,15 @@ class TxPatternEngine:
|
|
|
672
672
|
model: Optional[SpringSemanticModel] = None,
|
|
673
673
|
) -> list[SpringFinding]:
|
|
674
674
|
all_findings: list[SpringFinding] = []
|
|
675
|
+
self._last_analysis_errors: list[str] = []
|
|
675
676
|
for pattern in self.patterns:
|
|
676
677
|
try:
|
|
677
678
|
found = _call_pattern_analyze(pattern, cir, tx_index, root, model)
|
|
678
679
|
all_findings.extend(found)
|
|
679
|
-
except Exception:
|
|
680
|
-
|
|
680
|
+
except Exception as exc:
|
|
681
|
+
self._last_analysis_errors.append(
|
|
682
|
+
f"{pattern.pattern_id}: {type(exc).__name__}: {exc}"
|
|
683
|
+
)
|
|
681
684
|
deduped = deduplicate_findings(all_findings)
|
|
682
685
|
return sorted(deduped, key=lambda f: (SEVERITY_ORDER.get(f.severity, 9), f.symbol))
|
|
683
686
|
|
|
@@ -721,15 +724,19 @@ def run_tx_audit(
|
|
|
721
724
|
|
|
722
725
|
_spring_detected = tx_index.stats()["total"] > 0 or bool(model.bean_graph.beans)
|
|
723
726
|
|
|
727
|
+
_tx_limitations = [
|
|
728
|
+
"Self-invocation via this.method() not detected — requires AST-level analysis",
|
|
729
|
+
"Dynamic dispatch (interface/polymorphic calls) may produce incomplete call chains",
|
|
730
|
+
]
|
|
731
|
+
for _err in getattr(engine, "_last_analysis_errors", []):
|
|
732
|
+
_tx_limitations.append(f"PATTERN_ERROR: {_err}")
|
|
733
|
+
|
|
724
734
|
result = SpringAuditResult(
|
|
725
735
|
repo_id=getattr(cir, "cir_hash", "")[:16],
|
|
726
736
|
spring_detected=_spring_detected,
|
|
727
737
|
scope="tx",
|
|
728
738
|
findings=findings,
|
|
729
|
-
limitations=
|
|
730
|
-
"Self-invocation via this.method() not detected — requires AST-level analysis",
|
|
731
|
-
"Dynamic dispatch (interface/polymorphic calls) may produce incomplete call chains",
|
|
732
|
-
],
|
|
739
|
+
limitations=_tx_limitations,
|
|
733
740
|
metadata={
|
|
734
741
|
"symbols_analyzed": len(getattr(cir, "symbols", [])),
|
|
735
742
|
"tx_boundaries_found": tx_index.stats()["total"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.35.
|
|
3
|
+
Version: 1.35.22
|
|
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
|
|
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
42
42
|
|
|
43
|
-

|
|
44
44
|

|
|
45
45
|
|
|
46
46
|
---
|
|
@@ -114,7 +114,7 @@ pipx install sourcecode
|
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
116
|
sourcecode version
|
|
117
|
-
# sourcecode 1.35.
|
|
117
|
+
# sourcecode 1.35.20
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
---
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=p3pMcJ60xxF18F5C5hvsyn5B4uh1rOmKBO_RC94DPAA,104
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=qh749a7ykPtGmQI1MR9y6j8TtL_jBdVYFx9YRsLqOMw,44121
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
|
|
@@ -7,7 +7,7 @@ sourcecode/cache.py,sha256=wAyPrXN5DqiGivnMpeEuun2xHDKfBer2_oBsh6kj_vc,30447
|
|
|
7
7
|
sourcecode/canonical_ir.py,sha256=uwpwCnJxMh_eiIVg4cOLv7-aZthvmDFcG4azCOycLkw,24281
|
|
8
8
|
sourcecode/cir_graphs.py,sha256=rZi8JV4ZrAa2WSCeyNa4JIEKQ_yZzDZTsrvVz2KfuKA,8919
|
|
9
9
|
sourcecode/classifier.py,sha256=2lYoSH3vOTkXZYPU7Go2WIet1-IuNzTWVhc-ULnXtgw,8024
|
|
10
|
-
sourcecode/cli.py,sha256=
|
|
10
|
+
sourcecode/cli.py,sha256=wy5-T6Ba_hvqJoFkSROCJxOpsy_VzbYI98TlJIEeGW0,238063
|
|
11
11
|
sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
|
|
12
12
|
sourcecode/confidence_analyzer.py,sha256=_jckZSxksV-OU38vbkxfVNBnWCtlCq8Vwfg23x1uspA,19054
|
|
13
13
|
sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
|
|
@@ -15,7 +15,7 @@ sourcecode/context_summarizer.py,sha256=zlbm8ytdvJToohU108-dwBmEl52xl0gXpf6PZBOW
|
|
|
15
15
|
sourcecode/contract_model.py,sha256=nRxJKPMs1VHwFTa8AVXhGmaLjti3Lr2sjHDpWgv1bfE,3917
|
|
16
16
|
sourcecode/contract_pipeline.py,sha256=gvTdDniedm_mjq4vaHqnBY2UkQ0s00gtXqzTLILNXHc,28719
|
|
17
17
|
sourcecode/coverage_parser.py,sha256=q0LeZJaX1bnntLu-ImksdBsMlpsVmk_iUfSaB4eaJGo,19702
|
|
18
|
-
sourcecode/dependency_analyzer.py,sha256=
|
|
18
|
+
sourcecode/dependency_analyzer.py,sha256=gvFJf9gHyUGRia3tdPz8s0aX2Re6aohMhb40uFEbjp0,60420
|
|
19
19
|
sourcecode/doc_analyzer.py,sha256=05bjTUbDbmnbajD_cgRnACzS8T7xxBKVX4CjkJlhZg8,24411
|
|
20
20
|
sourcecode/entrypoint_classifier.py,sha256=jhTYlyqDJH2AtdEcLVaRU3lYRTJuF8DkxVzl4-W3zWE,5322
|
|
21
21
|
sourcecode/env_analyzer.py,sha256=aNTyYgQk5noJDfJU6FmasmESOHfiomyJw5EvZqjy6qc,22213
|
|
@@ -40,7 +40,7 @@ sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,
|
|
|
40
40
|
sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
|
|
41
41
|
sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
|
|
42
42
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
43
|
-
sourcecode/repository_ir.py,sha256=
|
|
43
|
+
sourcecode/repository_ir.py,sha256=5meIvsVN4WlllStxIglZznILKcI0Q2t2XMLB9aVT3uc,169561
|
|
44
44
|
sourcecode/ris.py,sha256=RcqLVwC-doFcKKViYDkCjZLBqf_wzLES7-F6vHEeWzE,20419
|
|
45
45
|
sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
|
|
46
46
|
sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
|
|
@@ -51,9 +51,9 @@ sourcecode/spring_event_topology.py,sha256=LvGv5RXtU_O-fVB_OO9eDD2UmZM72Jn2oUHgO
|
|
|
51
51
|
sourcecode/spring_findings.py,sha256=8V91iHOg9hFgg6tLLl4FSsgrF-dBqOcO2s-K5sD_goA,5417
|
|
52
52
|
sourcecode/spring_impact.py,sha256=Ohm2k3W4Wts8Kx8Z7DIM-J-cwGtTJBWKFBsX-WkupBQ,32943
|
|
53
53
|
sourcecode/spring_model.py,sha256=IzMcM5ftw1_EHG3FGUDT7qdAMpo3eqbAE1LRuasfr_4,14739
|
|
54
|
-
sourcecode/spring_security_audit.py,sha256=
|
|
54
|
+
sourcecode/spring_security_audit.py,sha256=AmUkqoExkNZ3YxxZf9TwkwX-f7P_SETm0QC7VqEAqh4,20618
|
|
55
55
|
sourcecode/spring_semantic.py,sha256=CiAf77p48-RFrUF0zbgww4w2Xigrbo1t5M3ZCDIfV_g,12032
|
|
56
|
-
sourcecode/spring_tx_analyzer.py,sha256=
|
|
56
|
+
sourcecode/spring_tx_analyzer.py,sha256=u4_ckdEFZUiIsHdUX4OaIhnvoTdAwrxNTFweG6vc7wE,30526
|
|
57
57
|
sourcecode/summarizer.py,sha256=YspHEVeYJVmltq0FMtGZF8kIP3qiR2KLcanGL6Y7uTI,20747
|
|
58
58
|
sourcecode/tree_utils.py,sha256=8GAkIfQAsvtEudIeW1l4ooH_oRtrWR8cpJQJsEa_Pfw,2093
|
|
59
59
|
sourcecode/workspace.py,sha256=X_6NmNnitvT3_38V-JDChydo_sR68s249hLFlrQskU0,8271
|
|
@@ -80,9 +80,9 @@ sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG6
|
|
|
80
80
|
sourcecode/detectors/tooling.py,sha256=8CKbtxwQoABP-WyBRNmdAmHDOvAH57AR1cF4UKuWEdQ,2074
|
|
81
81
|
sourcecode/mcp/__init__.py,sha256=XU4HfRGbdid8wdUA0x_4f7uKZD1z3mv_XUY_WU_T9Mw,179
|
|
82
82
|
sourcecode/mcp/orchestrator.py,sha256=BMi1D6liJHI3DXiaC8yeBLLP0wXajpCP3-vnRGqrvnw,26850
|
|
83
|
-
sourcecode/mcp/registry.py,sha256=
|
|
83
|
+
sourcecode/mcp/registry.py,sha256=XeshSuT6NMmeUZ2GCzNVcKcr-2Ljoj4qO-lvSrg17EM,63135
|
|
84
84
|
sourcecode/mcp/runner.py,sha256=-Dp2qPGRkfNTVen6bKh7WtzQqpcEtsrXoiuajvshlKk,2866
|
|
85
|
-
sourcecode/mcp/server.py,sha256=
|
|
85
|
+
sourcecode/mcp/server.py,sha256=Zapr4lY0i4tqSXY2BfA283VzStHTohNr9N0uMnRSIIA,59911
|
|
86
86
|
sourcecode/mcp/onboarding/__init__.py,sha256=sj2PWqEBmMc4zBNkomg89WtL0M6S7A9yb7_wAuSWNP4,66
|
|
87
87
|
sourcecode/mcp/onboarding/applier.py,sha256=B9CneieWTpaDSDIyW3S5nrlRlBpvfqUcgi93-mm_ApQ,2135
|
|
88
88
|
sourcecode/mcp/onboarding/backup.py,sha256=ihqGOR8QTX8HASRSEDyfFyXr5bkXrygPHamv4p9KTmk,1452
|
|
@@ -94,8 +94,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
94
94
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
95
95
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
96
96
|
sourcecode/telemetry/transport.py,sha256=QSslxIwij8YkRWcVvxykODDrkiN_GAAEu3dUP7KIWeE,1651
|
|
97
|
-
sourcecode-1.35.
|
|
98
|
-
sourcecode-1.35.
|
|
99
|
-
sourcecode-1.35.
|
|
100
|
-
sourcecode-1.35.
|
|
101
|
-
sourcecode-1.35.
|
|
97
|
+
sourcecode-1.35.22.dist-info/METADATA,sha256=myCZutK9p4r8d-O38SYo4et7pkpwASrkuJOnr_WIAac,21297
|
|
98
|
+
sourcecode-1.35.22.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
99
|
+
sourcecode-1.35.22.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
100
|
+
sourcecode-1.35.22.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
101
|
+
sourcecode-1.35.22.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|