sourcecode 1.32.4__py3-none-any.whl → 1.32.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 +1 -1
- sourcecode/cache.py +10 -1
- sourcecode/cli.py +105 -3
- sourcecode/license.py +1 -0
- sourcecode/ris.py +7 -2
- {sourcecode-1.32.4.dist-info → sourcecode-1.32.6.dist-info}/METADATA +1 -1
- {sourcecode-1.32.4.dist-info → sourcecode-1.32.6.dist-info}/RECORD +10 -10
- {sourcecode-1.32.4.dist-info → sourcecode-1.32.6.dist-info}/WHEEL +0 -0
- {sourcecode-1.32.4.dist-info → sourcecode-1.32.6.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.32.4.dist-info → sourcecode-1.32.6.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cache.py
CHANGED
|
@@ -72,6 +72,11 @@ SCHEMA_VERSION: str = "2"
|
|
|
72
72
|
#: Bump to invalidate all L1 core caches (independent of snapshot version).
|
|
73
73
|
CORE_SCHEMA_VERSION: str = "1"
|
|
74
74
|
|
|
75
|
+
#: Bump when analysis logic or output schema changes — NOT on every package release.
|
|
76
|
+
#: This is the stable part of the L1 core cache key. Package version bumps (patch,
|
|
77
|
+
#: minor) must NOT bump this value unless the cached data format actually changed.
|
|
78
|
+
ANALYZER_CACHE_VERSION: str = "1"
|
|
79
|
+
|
|
75
80
|
#: Fields eligible for CAS deduplication (applied to top-level JSON dict keys).
|
|
76
81
|
_CAS_FIELDS: frozenset[str] = frozenset([
|
|
77
82
|
"file_paths",
|
|
@@ -511,7 +516,7 @@ def _cas_store_blob(cache_d: Path, serialised: str) -> str:
|
|
|
511
516
|
path = _cas_path(cache_d, blob_hash)
|
|
512
517
|
if not path.exists():
|
|
513
518
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
514
|
-
path
|
|
519
|
+
_atomic_write(path, gzip.compress(raw, compresslevel=6))
|
|
515
520
|
return blob_hash
|
|
516
521
|
|
|
517
522
|
|
|
@@ -642,7 +647,11 @@ def _gc(cache_d: Path) -> None:
|
|
|
642
647
|
# ── Pass 3: total size cap ──────────────────────────────────────────
|
|
643
648
|
if max_size_bytes > 0:
|
|
644
649
|
size_candidates = [p for p in surviving if p.exists()]
|
|
650
|
+
# Include CAS blobs in the size budget calculation
|
|
651
|
+
cas_d_sz = _cas_dir(cache_d)
|
|
652
|
+
cas_files = list(cas_d_sz.glob("*.gz")) if cas_d_sz.exists() else []
|
|
645
653
|
total = sum(p.stat().st_size for p in size_candidates if not p.name.startswith("view-"))
|
|
654
|
+
total += sum(p.stat().st_size for p in cas_files if p.exists())
|
|
646
655
|
if total > max_size_bytes:
|
|
647
656
|
# Sort oldest-first; evict core+snapshot files until under budget
|
|
648
657
|
size_candidates.sort(key=lambda p: p.stat().st_mtime)
|
sourcecode/cli.py
CHANGED
|
@@ -208,6 +208,8 @@ _SUBCOMMANDS: frozenset[str] = frozenset(
|
|
|
208
208
|
"onboard", "modernize", "fix-bug", "review-pr",
|
|
209
209
|
# License
|
|
210
210
|
"activate",
|
|
211
|
+
# Cache observability
|
|
212
|
+
"cache",
|
|
211
213
|
}
|
|
212
214
|
)
|
|
213
215
|
|
|
@@ -382,6 +384,18 @@ def _get_command_with_preprocessing(typer_instance: Any) -> Any:
|
|
|
382
384
|
# Refresh help text at invocation time so it reflects current license state.
|
|
383
385
|
cmd.help = _build_help_text()
|
|
384
386
|
|
|
387
|
+
# Add [Pro] badge to Pro-gated option help strings for free-tier users.
|
|
388
|
+
try:
|
|
389
|
+
from sourcecode.license import is_pro as _lp_is_pro
|
|
390
|
+
except Exception:
|
|
391
|
+
_lp_is_pro = False
|
|
392
|
+
if not _lp_is_pro:
|
|
393
|
+
_PRO_OPTS = {"full"}
|
|
394
|
+
for _param in cmd.params:
|
|
395
|
+
if getattr(_param, "name", None) in _PRO_OPTS and getattr(_param, "help", None):
|
|
396
|
+
if "[Pro]" not in _param.help:
|
|
397
|
+
_param.help = _param.help + " [dim][Pro][/dim]"
|
|
398
|
+
|
|
385
399
|
_orig_cmd_main = cmd.main
|
|
386
400
|
|
|
387
401
|
def _cmd_main(args: Optional[list[str]] = None, **kwargs: Any) -> Any:
|
|
@@ -412,6 +426,9 @@ app.add_typer(telemetry_app, name="telemetry")
|
|
|
412
426
|
mcp_app = typer.Typer(help="MCP integration: setup, status, serve, remove.", rich_markup_mode="rich")
|
|
413
427
|
app.add_typer(mcp_app, name="mcp")
|
|
414
428
|
|
|
429
|
+
cache_app = typer.Typer(help="Cache inspection and management.", rich_markup_mode="rich")
|
|
430
|
+
app.add_typer(cache_app, name="cache")
|
|
431
|
+
|
|
415
432
|
|
|
416
433
|
def _maybe_ask_consent() -> None:
|
|
417
434
|
"""Show first-run consent prompt once, on interactive TTYs only."""
|
|
@@ -1064,7 +1081,6 @@ def main(
|
|
|
1064
1081
|
# Only cache when target IS the git repo root (not a subdir of one),
|
|
1065
1082
|
# to avoid polluting sub-project directories used in tests.
|
|
1066
1083
|
if _git_sha and (target / ".git").exists():
|
|
1067
|
-
from sourcecode import __version__ as _sc_version
|
|
1068
1084
|
_excl_key = (
|
|
1069
1085
|
",".join(sorted(e.strip() for e in exclude.split(",") if e.strip()))
|
|
1070
1086
|
if exclude else ""
|
|
@@ -1072,8 +1088,10 @@ def main(
|
|
|
1072
1088
|
|
|
1073
1089
|
# ── Core (analysis) flags: affect which analyzers run + scan config ──
|
|
1074
1090
|
# Use effective_depth (not raw depth) so Java auto-adjustment is captured.
|
|
1091
|
+
# acv = ANALYZER_CACHE_VERSION — bumped only when analysis logic/schema
|
|
1092
|
+
# changes, NOT on every package release. Prevents patch-bump cache wipes.
|
|
1075
1093
|
_core_flags_str = (
|
|
1076
|
-
f"
|
|
1094
|
+
f"acv={_cache_mod.ANALYZER_CACHE_VERSION},"
|
|
1077
1095
|
f"dep={dependencies},gm={graph_modules},"
|
|
1078
1096
|
f"docs={docs},fm={full_metrics},sem={semantics},"
|
|
1079
1097
|
f"arch={architecture},gc={git_context},em={env_map},"
|
|
@@ -1436,12 +1454,23 @@ def main(
|
|
|
1436
1454
|
if dependency_analyzer is not None
|
|
1437
1455
|
else None
|
|
1438
1456
|
)
|
|
1457
|
+
# Free-tier node cap: limit graph size to 10 nodes unless Pro.
|
|
1458
|
+
_effective_max_nodes = max_nodes
|
|
1459
|
+
try:
|
|
1460
|
+
from sourcecode.license import is_pro as _lp_is_pro_gm
|
|
1461
|
+
except Exception:
|
|
1462
|
+
_lp_is_pro_gm = False
|
|
1463
|
+
_FREE_NODE_CAP = 10
|
|
1464
|
+
if not _lp_is_pro_gm and graph_analyzer is not None:
|
|
1465
|
+
if _effective_max_nodes is None or _effective_max_nodes > _FREE_NODE_CAP:
|
|
1466
|
+
_effective_max_nodes = _FREE_NODE_CAP
|
|
1467
|
+
|
|
1439
1468
|
module_graph = (
|
|
1440
1469
|
graph_analyzer.merge_graphs(
|
|
1441
1470
|
module_graphs,
|
|
1442
1471
|
detail=graph_detail_typed,
|
|
1443
1472
|
edge_kinds=parsed_graph_edges,
|
|
1444
|
-
max_nodes=
|
|
1473
|
+
max_nodes=_effective_max_nodes,
|
|
1445
1474
|
entry_points=entry_points,
|
|
1446
1475
|
)
|
|
1447
1476
|
if graph_analyzer is not None
|
|
@@ -1561,6 +1590,15 @@ def main(
|
|
|
1561
1590
|
semantic_summary=sem_sum,
|
|
1562
1591
|
)
|
|
1563
1592
|
|
|
1593
|
+
# Free-tier node cap for --semantics: limit semantic_symbols to 10 unless Pro.
|
|
1594
|
+
if semantic_analyzer is not None and sm.semantic_symbols:
|
|
1595
|
+
try:
|
|
1596
|
+
from sourcecode.license import is_pro as _lp_is_pro_sem
|
|
1597
|
+
except Exception:
|
|
1598
|
+
_lp_is_pro_sem = False
|
|
1599
|
+
if not _lp_is_pro_sem and len(sm.semantic_symbols) > _FREE_NODE_CAP:
|
|
1600
|
+
sm = replace(sm, semantic_symbols=sm.semantic_symbols[:_FREE_NODE_CAP])
|
|
1601
|
+
|
|
1564
1602
|
# Runtime architecture — classify workspace packages for structural summaries
|
|
1565
1603
|
if workspace_analysis.workspaces:
|
|
1566
1604
|
from sourcecode.runtime_classifier import RuntimeClassifier
|
|
@@ -4124,6 +4162,70 @@ def mcp_remove(
|
|
|
4124
4162
|
typer.echo(" Re-add: sourcecode mcp init")
|
|
4125
4163
|
|
|
4126
4164
|
|
|
4165
|
+
# ── Cache subcommands ─────────────────────────────────────────────────────────
|
|
4166
|
+
|
|
4167
|
+
|
|
4168
|
+
@cache_app.command("status")
|
|
4169
|
+
def cache_status_cmd(
|
|
4170
|
+
path: Path = typer.Argument(Path("."), help="Repository path (default: current directory)"),
|
|
4171
|
+
json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
|
|
4172
|
+
) -> None:
|
|
4173
|
+
"""Show cache statistics for a repository."""
|
|
4174
|
+
from sourcecode import cache as _cm
|
|
4175
|
+
target = Path(path).resolve()
|
|
4176
|
+
stats = _cm.status(target)
|
|
4177
|
+
if json_output:
|
|
4178
|
+
import json as _j
|
|
4179
|
+
typer.echo(_j.dumps(stats, indent=2, ensure_ascii=False))
|
|
4180
|
+
else:
|
|
4181
|
+
typer.echo(f"Cache dir: {stats['cache_dir']}")
|
|
4182
|
+
typer.echo(f"Cores: {stats['cores']}")
|
|
4183
|
+
typer.echo(f"Snapshots: {stats['snapshots']}")
|
|
4184
|
+
typer.echo(f"Views: {stats['views']}")
|
|
4185
|
+
typer.echo(f"CAS blobs: {stats['cas_blobs']}")
|
|
4186
|
+
typer.echo(f"Total size: {stats['total_size_mb']} MB")
|
|
4187
|
+
|
|
4188
|
+
|
|
4189
|
+
@cache_app.command("clear")
|
|
4190
|
+
def cache_clear_cmd(
|
|
4191
|
+
path: Path = typer.Argument(Path("."), help="Repository path (default: current directory)"),
|
|
4192
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
|
|
4193
|
+
) -> None:
|
|
4194
|
+
"""Delete all cached snapshots for a repository."""
|
|
4195
|
+
from sourcecode import cache as _cm
|
|
4196
|
+
target = Path(path).resolve()
|
|
4197
|
+
if not yes:
|
|
4198
|
+
typer.confirm(f"Delete all cache files for {target}?", abort=True)
|
|
4199
|
+
removed = _cm.clear(target)
|
|
4200
|
+
typer.echo(f"Removed {removed} file(s).")
|
|
4201
|
+
|
|
4202
|
+
|
|
4203
|
+
@cache_app.command("warm")
|
|
4204
|
+
def cache_warm_cmd(
|
|
4205
|
+
path: Path = typer.Argument(Path("."), help="Repository path to warm (default: current directory)"),
|
|
4206
|
+
compact: bool = typer.Option(True, "--compact/--no-compact", help="Warm compact view (default: on)."),
|
|
4207
|
+
agent: bool = typer.Option(False, "--agent", help="Also warm agent view."),
|
|
4208
|
+
) -> None:
|
|
4209
|
+
"""Pre-populate the cache by running a fresh analysis."""
|
|
4210
|
+
import subprocess as _sub
|
|
4211
|
+
import sys as _sys
|
|
4212
|
+
target = Path(path).resolve()
|
|
4213
|
+
typer.echo(f"Warming cache for {target} …", err=True)
|
|
4214
|
+
cmd = [_sys.executable, "-m", "sourcecode", str(target)]
|
|
4215
|
+
if compact:
|
|
4216
|
+
cmd.append("--compact")
|
|
4217
|
+
if agent:
|
|
4218
|
+
cmd.append("--agent")
|
|
4219
|
+
result = _sub.run(cmd, capture_output=True, text=True)
|
|
4220
|
+
if result.returncode == 0:
|
|
4221
|
+
typer.echo("Cache warmed.", err=True)
|
|
4222
|
+
else:
|
|
4223
|
+
typer.echo(f"Warm failed (exit {result.returncode}).", err=True)
|
|
4224
|
+
if result.stderr:
|
|
4225
|
+
typer.echo(result.stderr.strip(), err=True)
|
|
4226
|
+
raise typer.Exit(code=result.returncode)
|
|
4227
|
+
|
|
4228
|
+
|
|
4127
4229
|
# ── Entry point ───────────────────────────────────────────────────────────────
|
|
4128
4230
|
|
|
4129
4231
|
def main_entry() -> None:
|
sourcecode/license.py
CHANGED
|
@@ -248,6 +248,7 @@ def require_feature(feature_name: str) -> None:
|
|
|
248
248
|
f"'{display}' requires a Pro license. "
|
|
249
249
|
"Run: sourcecode activate <license_key>"
|
|
250
250
|
),
|
|
251
|
+
"upgrade_hint": "sourcecode activate <license_key>",
|
|
251
252
|
}
|
|
252
253
|
sys.stdout.write(json.dumps(payload, ensure_ascii=False) + "\n")
|
|
253
254
|
sys.stdout.flush()
|
sourcecode/ris.py
CHANGED
|
@@ -329,10 +329,15 @@ def update_ris_api_surface(repo_root: Path, endpoints_data: dict) -> None:
|
|
|
329
329
|
# ---------------------------------------------------------------------------
|
|
330
330
|
|
|
331
331
|
def _current_git_head(repo_root: Path) -> str:
|
|
332
|
-
"""Return current HEAD SHA. Returns '' on any error or non-git directory.
|
|
332
|
+
"""Return current HEAD short SHA. Returns '' on any error or non-git directory.
|
|
333
|
+
|
|
334
|
+
Uses --short to match the format stored in the RIS and used by cli.py
|
|
335
|
+
cache key computation — both sides must use the same format or staleness
|
|
336
|
+
checks will always return True.
|
|
337
|
+
"""
|
|
333
338
|
try:
|
|
334
339
|
result = subprocess.run(
|
|
335
|
-
["git", "-C", str(repo_root), "rev-parse", "HEAD"],
|
|
340
|
+
["git", "-C", str(repo_root), "rev-parse", "--short", "HEAD"],
|
|
336
341
|
capture_output=True,
|
|
337
342
|
text=True,
|
|
338
343
|
timeout=2,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=KgyhBn2jfjwXzdBpdStMZoNfqCUlHHAwk1wuU3Nez1M,103
|
|
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
|
|
5
5
|
sourcecode/ast_extractor.py,sha256=_btmeOJIe3t-NicF94D5ZAesa2YIJ0_QNExGnbHxGFE,50578
|
|
6
|
-
sourcecode/cache.py,sha256=
|
|
6
|
+
sourcecode/cache.py,sha256=h1BT-9PG_7HK---ZzH0j5u3PN0dz2s6IRAUOjQIPYH4,28055
|
|
7
7
|
sourcecode/cache.tmp_new,sha256=-IvV7CojiZjqeKMln1m-lqI0QVA2uFGWmYir4XRFOUk,27970
|
|
8
8
|
sourcecode/canonical_ir.py,sha256=_HM3AUmKSdna9u4dCoU6rpgSA6HdF8gzOKZykIUCNGY,23277
|
|
9
9
|
sourcecode/classifier.py,sha256=2lYoSH3vOTkXZYPU7Go2WIet1-IuNzTWVhc-ULnXtgw,8024
|
|
10
|
-
sourcecode/cli.py,sha256=
|
|
10
|
+
sourcecode/cli.py,sha256=PJbs8ltIdsBmedJlBJ6HG9UCRBLDtUhG_eIqyazP_sU,170992
|
|
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
|
|
@@ -23,7 +23,7 @@ sourcecode/file_classifier.py,sha256=QrYm7MlG29HQdAR1WOfpnIIBysAz62c5coz9eQ76meo
|
|
|
23
23
|
sourcecode/flow_analyzer.py,sha256=dSiuY4w49k29jW_EPXUOND9B5uVbuCA7kjnuHi-pIWA,28781
|
|
24
24
|
sourcecode/git_analyzer.py,sha256=JStxTQXNjBWi_wLdwhsZs9mT-v50cSJIz4Agzn6Kh9I,13362
|
|
25
25
|
sourcecode/graph_analyzer.py,sha256=iUK-7pSV-cvGqqD2hENdYmhnm0wcXFEyK-xnu5ul8OU,62515
|
|
26
|
-
sourcecode/license.py,sha256=
|
|
26
|
+
sourcecode/license.py,sha256=cz6FIqo8QjPeLmXiBWoo256gfqsotgbHHgMMEwR5pjw,11439
|
|
27
27
|
sourcecode/mcp_nudge.py,sha256=5ELU_ixzh6uA83NXLOZT8h00OhL53okfQdji3jyKOjg,2917
|
|
28
28
|
sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
|
|
29
29
|
sourcecode/output_budget.py,sha256=43307mJEyUPU3MI-QEQoVxrcAvNyUzdzF_SAPgisBQE,6603
|
|
@@ -36,7 +36,7 @@ sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
|
|
|
36
36
|
sourcecode/relevance_scorer.py,sha256=MYF4FFkveAQps9SmTeTlh6ODiBz2F--_hWNeHMLtUHQ,8405
|
|
37
37
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
38
38
|
sourcecode/repository_ir.py,sha256=-NjBQUT7zyya4ng8Hq0-ChoiHZkUif9lr-Q878gmj8M,153163
|
|
39
|
-
sourcecode/ris.py,sha256=
|
|
39
|
+
sourcecode/ris.py,sha256=CqNfiIbzfHZQZx0mWLjyVD4o8u5ZLnbv3Ia7D7gYhQY,14212
|
|
40
40
|
sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
|
|
41
41
|
sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
|
|
42
42
|
sourcecode/schema.py,sha256=aHNXDf8LGyUC8ZDE_VS9kiskC2-Oswhi_WnpdGy6HDw,24897
|
|
@@ -80,8 +80,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
80
80
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
81
81
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
82
82
|
sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
|
|
83
|
-
sourcecode-1.32.
|
|
84
|
-
sourcecode-1.32.
|
|
85
|
-
sourcecode-1.32.
|
|
86
|
-
sourcecode-1.32.
|
|
87
|
-
sourcecode-1.32.
|
|
83
|
+
sourcecode-1.32.6.dist-info/METADATA,sha256=Aa7pmdWB0T8LkQYd6rihJOQQwE1Ts_nJ7AHUyYT2gF0,19120
|
|
84
|
+
sourcecode-1.32.6.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
85
|
+
sourcecode-1.32.6.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
86
|
+
sourcecode-1.32.6.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
87
|
+
sourcecode-1.32.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|