sourcecode 1.32.5__py3-none-any.whl → 1.32.7__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 +54 -2
- sourcecode/license.py +1 -0
- sourcecode/prepare_context.py +77 -0
- sourcecode/ris.py +13 -2
- {sourcecode-1.32.5.dist-info → sourcecode-1.32.7.dist-info}/METADATA +1 -1
- {sourcecode-1.32.5.dist-info → sourcecode-1.32.7.dist-info}/RECORD +10 -10
- {sourcecode-1.32.5.dist-info → sourcecode-1.32.7.dist-info}/WHEEL +0 -0
- {sourcecode-1.32.5.dist-info → sourcecode-1.32.7.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.32.5.dist-info → sourcecode-1.32.7.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -384,6 +384,18 @@ def _get_command_with_preprocessing(typer_instance: Any) -> Any:
|
|
|
384
384
|
# Refresh help text at invocation time so it reflects current license state.
|
|
385
385
|
cmd.help = _build_help_text()
|
|
386
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
|
+
|
|
387
399
|
_orig_cmd_main = cmd.main
|
|
388
400
|
|
|
389
401
|
def _cmd_main(args: Optional[list[str]] = None, **kwargs: Any) -> Any:
|
|
@@ -1442,12 +1454,23 @@ def main(
|
|
|
1442
1454
|
if dependency_analyzer is not None
|
|
1443
1455
|
else None
|
|
1444
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
|
+
|
|
1445
1468
|
module_graph = (
|
|
1446
1469
|
graph_analyzer.merge_graphs(
|
|
1447
1470
|
module_graphs,
|
|
1448
1471
|
detail=graph_detail_typed,
|
|
1449
1472
|
edge_kinds=parsed_graph_edges,
|
|
1450
|
-
max_nodes=
|
|
1473
|
+
max_nodes=_effective_max_nodes,
|
|
1451
1474
|
entry_points=entry_points,
|
|
1452
1475
|
)
|
|
1453
1476
|
if graph_analyzer is not None
|
|
@@ -1567,6 +1590,15 @@ def main(
|
|
|
1567
1590
|
semantic_summary=sem_sum,
|
|
1568
1591
|
)
|
|
1569
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
|
+
|
|
1570
1602
|
# Runtime architecture — classify workspace packages for structural summaries
|
|
1571
1603
|
if workspace_analysis.workspaces:
|
|
1572
1604
|
from sourcecode.runtime_classifier import RuntimeClassifier
|
|
@@ -3721,6 +3753,24 @@ def config_cmd() -> None:
|
|
|
3721
3753
|
typer.echo(" sourcecode telemetry status")
|
|
3722
3754
|
|
|
3723
3755
|
|
|
3756
|
+
# ── cold-start (RIS bootstrap for external MCP and agents) ───────────────────
|
|
3757
|
+
|
|
3758
|
+
@app.command("cold-start")
|
|
3759
|
+
def cold_start_cmd(
|
|
3760
|
+
path: Path = typer.Argument(Path("."), help="Repository path (default: current directory)"),
|
|
3761
|
+
) -> None:
|
|
3762
|
+
"""Output Repository Intelligence Snapshot bootstrap context as JSON.
|
|
3763
|
+
|
|
3764
|
+
Returns instantly from persisted RIS — zero re-analysis cost.
|
|
3765
|
+
status: cold_start_ready | cold_start_stale | no_ris
|
|
3766
|
+
"""
|
|
3767
|
+
import json as _json
|
|
3768
|
+
from sourcecode.ris import get_cold_start_context as _gcs
|
|
3769
|
+
target = Path(path).resolve()
|
|
3770
|
+
result = _gcs(target)
|
|
3771
|
+
typer.echo(_json.dumps(result, indent=2, ensure_ascii=False))
|
|
3772
|
+
|
|
3773
|
+
|
|
3724
3774
|
# ── analyze (legacy alias) ────────────────────────────────────────────────────
|
|
3725
3775
|
|
|
3726
3776
|
@app.command("analyze", hidden=True)
|
|
@@ -4175,11 +4225,13 @@ def cache_warm_cmd(
|
|
|
4175
4225
|
agent: bool = typer.Option(False, "--agent", help="Also warm agent view."),
|
|
4176
4226
|
) -> None:
|
|
4177
4227
|
"""Pre-populate the cache by running a fresh analysis."""
|
|
4228
|
+
import shutil as _shutil
|
|
4178
4229
|
import subprocess as _sub
|
|
4179
4230
|
import sys as _sys
|
|
4180
4231
|
target = Path(path).resolve()
|
|
4181
4232
|
typer.echo(f"Warming cache for {target} …", err=True)
|
|
4182
|
-
|
|
4233
|
+
_sc_bin = _shutil.which("sourcecode") or _sys.argv[0]
|
|
4234
|
+
cmd = [_sc_bin, str(target)]
|
|
4183
4235
|
if compact:
|
|
4184
4236
|
cmd.append("--compact")
|
|
4185
4237
|
if agent:
|
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/prepare_context.py
CHANGED
|
@@ -734,6 +734,77 @@ class TaskContextBuilder:
|
|
|
734
734
|
def __init__(self, root: Path) -> None:
|
|
735
735
|
self.root = root
|
|
736
736
|
|
|
737
|
+
# ------------------------------------------------------------------
|
|
738
|
+
# RIS fast-path: serve onboard/explain from warm snapshot (<50ms)
|
|
739
|
+
# ------------------------------------------------------------------
|
|
740
|
+
|
|
741
|
+
def _try_ris_fast_path(self, task_name: str, spec: "TaskSpec") -> Optional[TaskOutput]:
|
|
742
|
+
"""Return TaskOutput from a warm RIS without running the full scan.
|
|
743
|
+
|
|
744
|
+
Only activated for onboard/explain when git HEAD matches stored RIS.
|
|
745
|
+
Falls through (returns None) on any error or cache miss.
|
|
746
|
+
"""
|
|
747
|
+
try:
|
|
748
|
+
import subprocess as _sp
|
|
749
|
+
from sourcecode.ris import load_ris as _lris
|
|
750
|
+
_ris = _lris(self.root)
|
|
751
|
+
if _ris is None or not _ris.compact_summary:
|
|
752
|
+
return None
|
|
753
|
+
_r = _sp.run(
|
|
754
|
+
["git", "-C", str(self.root), "rev-parse", "--short", "HEAD"],
|
|
755
|
+
capture_output=True, text=True, timeout=2,
|
|
756
|
+
)
|
|
757
|
+
if _r.returncode != 0 or _r.stdout.strip() != _ris.git_head:
|
|
758
|
+
return None
|
|
759
|
+
|
|
760
|
+
compact = _ris.compact_summary
|
|
761
|
+
struct = _ris.structural_map
|
|
762
|
+
|
|
763
|
+
entry_points = struct.get("entrypoints") or compact.get("entry_points") or []
|
|
764
|
+
controllers = struct.get("controllers") or []
|
|
765
|
+
services = struct.get("services") or []
|
|
766
|
+
|
|
767
|
+
_seen: set = set()
|
|
768
|
+
relevant: list[RelevantFile] = []
|
|
769
|
+
for fp in entry_points[:10]:
|
|
770
|
+
if isinstance(fp, str) and fp not in _seen:
|
|
771
|
+
_seen.add(fp)
|
|
772
|
+
relevant.append(RelevantFile(path=fp, role="entrypoint", score=1.0, reason="entry_point", why="Primary entry point"))
|
|
773
|
+
for fp in controllers[:8]:
|
|
774
|
+
if isinstance(fp, str) and fp not in _seen:
|
|
775
|
+
_seen.add(fp)
|
|
776
|
+
relevant.append(RelevantFile(path=fp, role="source", score=0.9, reason="controller", why="Controller"))
|
|
777
|
+
for fp in services[:8]:
|
|
778
|
+
if isinstance(fp, str) and fp not in _seen:
|
|
779
|
+
_seen.add(fp)
|
|
780
|
+
relevant.append(RelevantFile(path=fp, role="source", score=0.8, reason="service", why="Service layer"))
|
|
781
|
+
|
|
782
|
+
# compact_summary uses "analysis_gaps" (list[dict]) not "gaps" (list[str])
|
|
783
|
+
_raw_gaps = compact.get("gaps") or compact.get("analysis_gaps") or []
|
|
784
|
+
_gap_strs: list[str] = [
|
|
785
|
+
g.get("reason", str(g)) if isinstance(g, dict) else str(g)
|
|
786
|
+
for g in _raw_gaps
|
|
787
|
+
if g
|
|
788
|
+
]
|
|
789
|
+
|
|
790
|
+
return TaskOutput(
|
|
791
|
+
task=task_name,
|
|
792
|
+
goal=spec.goal,
|
|
793
|
+
project_summary=compact.get("project_summary") or compact.get("summary"),
|
|
794
|
+
architecture_summary=compact.get("architecture_summary"),
|
|
795
|
+
relevant_files=relevant,
|
|
796
|
+
suspected_areas=[],
|
|
797
|
+
improvement_opportunities=[],
|
|
798
|
+
test_gaps=[],
|
|
799
|
+
key_dependencies=compact.get("key_dependencies") or [],
|
|
800
|
+
code_notes_summary=None,
|
|
801
|
+
limitations=[],
|
|
802
|
+
confidence=compact.get("confidence") or compact.get("confidence_summary") or "high",
|
|
803
|
+
gaps=_gap_strs,
|
|
804
|
+
)
|
|
805
|
+
except Exception:
|
|
806
|
+
return None
|
|
807
|
+
|
|
737
808
|
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False, include_config: bool = False, all_gaps: bool = False) -> TaskOutput:
|
|
738
809
|
if task_name not in TASKS:
|
|
739
810
|
raise ValueError(
|
|
@@ -741,6 +812,12 @@ class TaskContextBuilder:
|
|
|
741
812
|
)
|
|
742
813
|
spec = TASKS[task_name]
|
|
743
814
|
|
|
815
|
+
# ── RIS fast-path (onboard / explain only) ────────────────────────────
|
|
816
|
+
if task_name in ("onboard", "explain") and not fast:
|
|
817
|
+
_warm = self._try_ris_fast_path(task_name, spec)
|
|
818
|
+
if _warm is not None:
|
|
819
|
+
return _warm
|
|
820
|
+
|
|
744
821
|
# ── 0. review-pr: git-first scope resolution (before any filesystem scan) ─
|
|
745
822
|
_pr_git_root: Optional[Path] = None
|
|
746
823
|
_pr_scope_files: Optional[list[str]] = None
|
sourcecode/ris.py
CHANGED
|
@@ -362,7 +362,8 @@ def get_cold_start_context(repo_root: Path) -> dict:
|
|
|
362
362
|
current_head = _current_git_head(repo_root)
|
|
363
363
|
stale = bool(current_head and ris.git_head and current_head != ris.git_head)
|
|
364
364
|
|
|
365
|
-
|
|
365
|
+
endpoints = ris.api_surface.get("endpoints", [])
|
|
366
|
+
result: dict = {
|
|
366
367
|
"status": "cold_start_stale" if stale else "cold_start_ready",
|
|
367
368
|
"repo_id": ris.repo_id,
|
|
368
369
|
"git_head": ris.git_head,
|
|
@@ -370,8 +371,18 @@ def get_cold_start_context(repo_root: Path) -> dict:
|
|
|
370
371
|
"last_updated_at": ris.last_updated_at,
|
|
371
372
|
"summary": ris.compact_summary,
|
|
372
373
|
"entrypoints": ris.structural_map.get("entrypoints", []),
|
|
373
|
-
"endpoints":
|
|
374
|
+
"endpoints": endpoints,
|
|
374
375
|
"hotspots": ris.git_context_snapshot.get("hotspots", []),
|
|
375
376
|
}
|
|
377
|
+
if not endpoints:
|
|
378
|
+
_is_java = (repo_root / "pom.xml").exists() or \
|
|
379
|
+
(repo_root / "build.gradle").exists() or \
|
|
380
|
+
(repo_root / "build.gradle.kts").exists()
|
|
381
|
+
if _is_java:
|
|
382
|
+
result["endpoints_hint"] = (
|
|
383
|
+
"Java repo detected but no endpoint index found. "
|
|
384
|
+
"Call get_endpoints (or: sourcecode endpoints <path>) to populate."
|
|
385
|
+
)
|
|
386
|
+
return result
|
|
376
387
|
except Exception:
|
|
377
388
|
return {"status": "no_ris"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=CIA1OF_63l9Sim-JyGiJzoNHlZEiYgzB15GOsoj6BTQ,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
|
|
@@ -7,7 +7,7 @@ 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=Wyh5KEdbaCNfzJxQir8qkeQ2mt6itoCzejXPMgaAOw0,171752
|
|
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,20 +23,20 @@ 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
|
|
30
30
|
sourcecode/path_filters.py,sha256=ROFRQ8eSLBEMiixK9f45-RO7um4VEEcjoD5AA4I427I,3739
|
|
31
31
|
sourcecode/pr_comment_renderer.py,sha256=smHslxiG14lrytCkq5nFrFu-qTHgA-t-LFYfdrfjz2o,14423
|
|
32
|
-
sourcecode/prepare_context.py,sha256=
|
|
32
|
+
sourcecode/prepare_context.py,sha256=uZo0r7QBXUmQdfTp3w_A1XI-OzFAlcoH7F76_p7IQjo,205311
|
|
33
33
|
sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
|
|
34
34
|
sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,12970
|
|
35
35
|
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=2pQcNN-5akweoBjjWAk_x3QpJ06wukQInmMlMj7WxwI,14737
|
|
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.7.dist-info/METADATA,sha256=wNzW6mw_L1RiPyaZTimkxsnvq3VentweBCARODFVWbE,19120
|
|
84
|
+
sourcecode-1.32.7.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
85
|
+
sourcecode-1.32.7.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
86
|
+
sourcecode-1.32.7.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
87
|
+
sourcecode-1.32.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|