sourcecode 1.35.16__py3-none-any.whl → 1.35.17__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 -13
- sourcecode/license.py +62 -1
- {sourcecode-1.35.16.dist-info → sourcecode-1.35.17.dist-info}/METADATA +1 -1
- {sourcecode-1.35.16.dist-info → sourcecode-1.35.17.dist-info}/RECORD +8 -8
- {sourcecode-1.35.16.dist-info → sourcecode-1.35.17.dist-info}/WHEEL +0 -0
- {sourcecode-1.35.16.dist-info → sourcecode-1.35.17.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.35.16.dist-info → sourcecode-1.35.17.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -197,9 +197,13 @@ Cold scan: 2–10s depending on repo size. Warm cache: 0.3–0.6s.
|
|
|
197
197
|
[dim]modernize (full) dead zones, tangles, full coupling[/dim]
|
|
198
198
|
[dim]fix-bug (full) complete risk-ranked file list[/dim]
|
|
199
199
|
[dim]review-pr (expanded) CI-grade PR review[/dim]
|
|
200
|
-
[dim]prepare-context delta incremental context for CI/CD[/dim]
|
|
200
|
+
[dim]prepare-context delta incremental context for CI/CD (30 free runs/repo)[/dim]
|
|
201
201
|
[dim]prepare-context generate-tests test gap analysis[/dim]
|
|
202
|
-
[dim]--full removes all truncation limits[/dim]
|
|
202
|
+
[dim]--full removes all truncation limits (free up to 500 files)[/dim]
|
|
203
|
+
[dim]--rank-by git-churn file volatility ranking via git history[/dim]
|
|
204
|
+
[dim]rich exports (HTML/PDF/CI) structured reports for CI and stakeholders[/dim]
|
|
205
|
+
[dim]multi-repo analysis cross-repository blast radius[/dim]
|
|
206
|
+
[dim]team snapshots shared org-level cache[/dim]
|
|
203
207
|
|
|
204
208
|
[dim cyan]→ sourcecode activate <key>[/dim cyan]
|
|
205
209
|
"""
|
|
@@ -573,7 +577,8 @@ GRAPH_EDGE_CHOICES = {"imports", "calls", "contains", "extends"}
|
|
|
573
577
|
DOCS_DEPTH_CHOICES = ["module", "symbols", "full"]
|
|
574
578
|
|
|
575
579
|
# ── Module-level constants ─────────────────────────────────────────────────────
|
|
576
|
-
_FREE_TIER_NODE_CAP: int =
|
|
580
|
+
_FREE_TIER_NODE_CAP: int = 50 # semantic cap for graph nodes and semantic symbols in free tier
|
|
581
|
+
_FREE_FULL_FILE_THRESHOLD: int = 500 # Java source files; above this --full requires Pro
|
|
577
582
|
_JAVA_MIN_SCAN_DEPTH: int = 12 # Maven src/main/java/<pkg>/<module>/File depth floor
|
|
578
583
|
_JVM_STACKS: frozenset[str] = frozenset({"java", "kotlin", "scala", "groovy"})
|
|
579
584
|
_IMPACT_PRIORITY_THRESHOLDS: list[tuple[float, str]] = [
|
|
@@ -878,6 +883,11 @@ def main(
|
|
|
878
883
|
)
|
|
879
884
|
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
880
885
|
|
|
886
|
+
# Pro gate for --rank-by git-churn: git history analysis is a Pro feature.
|
|
887
|
+
if rank_by == "git-churn":
|
|
888
|
+
from sourcecode.license import require_feature as _req_git_history
|
|
889
|
+
_req_git_history("git-history")
|
|
890
|
+
|
|
881
891
|
if symbol is not None and not symbol.strip():
|
|
882
892
|
_emit_error_json(
|
|
883
893
|
INVALID_INPUT_CODE,
|
|
@@ -911,10 +921,21 @@ def main(
|
|
|
911
921
|
)
|
|
912
922
|
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
913
923
|
|
|
914
|
-
# Pro gate for --full:
|
|
924
|
+
# Pro gate for --full: free tier allowed up to _FREE_FULL_FILE_THRESHOLD Java files.
|
|
915
925
|
if full:
|
|
916
|
-
from sourcecode.license import
|
|
917
|
-
|
|
926
|
+
from sourcecode.license import is_pro as _full_is_pro
|
|
927
|
+
if not _full_is_pro:
|
|
928
|
+
from itertools import islice as _islice
|
|
929
|
+
_full_check_path = Path(_get_detected_path()).resolve()
|
|
930
|
+
_java_count = sum(
|
|
931
|
+
1 for _ in _islice(
|
|
932
|
+
(p for p in _full_check_path.rglob("*.java") if ".git" not in p.parts),
|
|
933
|
+
_FREE_FULL_FILE_THRESHOLD + 1,
|
|
934
|
+
)
|
|
935
|
+
)
|
|
936
|
+
if _java_count > _FREE_FULL_FILE_THRESHOLD:
|
|
937
|
+
from sourcecode.license import require_feature as _req_full
|
|
938
|
+
_req_full("--full")
|
|
918
939
|
|
|
919
940
|
# P0-2 FIX: --compact and --full are mutually exclusive.
|
|
920
941
|
# compact is designed to be a bounded summary; --full removes truncation limits,
|
|
@@ -2633,14 +2654,34 @@ def prepare_context_cmd(
|
|
|
2633
2654
|
)
|
|
2634
2655
|
raise typer.Exit(code=1)
|
|
2635
2656
|
|
|
2636
|
-
# Pro gate: generate-tests
|
|
2637
|
-
|
|
2638
|
-
if task in _PRO_TASKS:
|
|
2657
|
+
# Pro gate: generate-tests requires Pro. delta allows 30 free runs per repo.
|
|
2658
|
+
if task == "generate-tests":
|
|
2639
2659
|
from sourcecode.license import require_feature as _require_feature
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2660
|
+
_require_feature("generate-tests")
|
|
2661
|
+
elif task == "delta":
|
|
2662
|
+
from sourcecode.license import is_pro as _delta_is_pro
|
|
2663
|
+
if not _delta_is_pro:
|
|
2664
|
+
from sourcecode.license import check_delta_free_tier as _check_delta
|
|
2665
|
+
_delta_allowed, _delta_used, _delta_remaining = _check_delta(str(path.resolve()))
|
|
2666
|
+
if not _delta_allowed:
|
|
2667
|
+
from sourcecode.license import require_feature as _require_feature_delta
|
|
2668
|
+
_require_feature_delta(
|
|
2669
|
+
"delta",
|
|
2670
|
+
extra_fields={
|
|
2671
|
+
"free_tier_note": (
|
|
2672
|
+
f"Free quota of {30} delta runs per repository exhausted."
|
|
2673
|
+
),
|
|
2674
|
+
"free_tier_alternative": "sourcecode prepare-context review-pr --since <ref>",
|
|
2675
|
+
},
|
|
2676
|
+
)
|
|
2677
|
+
# Within quota: emit a header note so CI logs show remaining runs.
|
|
2678
|
+
elif _delta_remaining <= 5:
|
|
2679
|
+
import sys as _sys_delta
|
|
2680
|
+
_sys_delta.stderr.write(
|
|
2681
|
+
f"[sourcecode] delta free tier: {_delta_remaining} run(s) remaining"
|
|
2682
|
+
f" (used {_delta_used}/{30}). Upgrade to Pro for unlimited CI runs.\n"
|
|
2683
|
+
)
|
|
2684
|
+
_sys_delta.stderr.flush()
|
|
2644
2685
|
|
|
2645
2686
|
# Validate --format: only "json" and "github-comment" are valid for prepare-context.
|
|
2646
2687
|
# "yaml" is intentionally NOT supported here (use main command for yaml output).
|
sourcecode/license.py
CHANGED
|
@@ -41,7 +41,9 @@ if _SUPABASE_URL != _DEFAULT_SUPABASE_URL:
|
|
|
41
41
|
|
|
42
42
|
_LICENSE_DIR: Path = Path.home() / ".sourcecode"
|
|
43
43
|
_LICENSE_FILE: Path = _LICENSE_DIR / "license.json"
|
|
44
|
+
_DELTA_RUNS_FILE: Path = _LICENSE_DIR / "delta_runs.json"
|
|
44
45
|
_CACHE_TTL_SECONDS: int = 86400 # 24 hours
|
|
46
|
+
_DELTA_FREE_LIMIT: int = 30
|
|
45
47
|
_LICENSE_KEY_RE = re.compile(r"^[A-Za-z0-9_\-]{1,200}$")
|
|
46
48
|
|
|
47
49
|
# ---------------------------------------------------------------------------
|
|
@@ -83,12 +85,37 @@ _FEATURE_INFO: dict[str, dict[str, str]] = {
|
|
|
83
85
|
"value": "Reduces test debt systematically across the entire codebase.",
|
|
84
86
|
},
|
|
85
87
|
"--full": {
|
|
86
|
-
"display": "--full flag",
|
|
88
|
+
"display": "--full flag (large repos)",
|
|
87
89
|
"description": (
|
|
88
90
|
"Removes truncation limits on transactional boundaries, DTO mappers, and large result sets."
|
|
91
|
+
" Free tier may use --full on repositories under 500 Java source files."
|
|
89
92
|
),
|
|
90
93
|
"value": "Essential for complete analysis of enterprise-scale codebases.",
|
|
91
94
|
},
|
|
95
|
+
"git-history": {
|
|
96
|
+
"display": "git history analysis",
|
|
97
|
+
"description": (
|
|
98
|
+
"Churn ranking, commit frequency per file, volatility signals over 90-day window."
|
|
99
|
+
),
|
|
100
|
+
"value": "Identifies which files change most — the highest-risk targets in any refactor.",
|
|
101
|
+
},
|
|
102
|
+
"multi-repo": {
|
|
103
|
+
"display": "multi-repo analysis",
|
|
104
|
+
"description": (
|
|
105
|
+
"Cross-repository dependency graphs, shared module impact, and org-level blast radius."
|
|
106
|
+
),
|
|
107
|
+
"value": "Required for microservices and monorepo architectures.",
|
|
108
|
+
},
|
|
109
|
+
"export-rich": {
|
|
110
|
+
"display": "rich exports (HTML/PDF/CI)",
|
|
111
|
+
"description": "Structured HTML reports, PDF exports, and CI-consumable risk summaries.",
|
|
112
|
+
"value": "Embed analysis into your CI pipeline or share with non-CLI stakeholders.",
|
|
113
|
+
},
|
|
114
|
+
"team-snapshots": {
|
|
115
|
+
"display": "team snapshot sharing",
|
|
116
|
+
"description": "Shared org-level snapshots and multi-user cache access.",
|
|
117
|
+
"value": "Eliminates cold-cache overhead across the entire engineering team.",
|
|
118
|
+
},
|
|
92
119
|
}
|
|
93
120
|
|
|
94
121
|
# ---------------------------------------------------------------------------
|
|
@@ -113,6 +140,40 @@ def _write_license_file(data: dict) -> None:
|
|
|
113
140
|
raise
|
|
114
141
|
|
|
115
142
|
|
|
143
|
+
def _read_delta_runs() -> dict:
|
|
144
|
+
try:
|
|
145
|
+
if _DELTA_RUNS_FILE.exists():
|
|
146
|
+
return json.loads(_DELTA_RUNS_FILE.read_text(encoding="utf-8"))
|
|
147
|
+
except Exception:
|
|
148
|
+
pass
|
|
149
|
+
return {}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def check_delta_free_tier(repo_path: str) -> "tuple[bool, int, int]":
|
|
153
|
+
"""Check and consume one delta free-tier run for repo_path.
|
|
154
|
+
|
|
155
|
+
Returns (allowed, runs_used, runs_remaining).
|
|
156
|
+
When allowed=True the run count is incremented atomically.
|
|
157
|
+
When allowed=False the quota is exhausted — caller should gate to Pro.
|
|
158
|
+
"""
|
|
159
|
+
import hashlib
|
|
160
|
+
key = hashlib.sha256(str(Path(repo_path).resolve()).encode()).hexdigest()[:16]
|
|
161
|
+
runs = _read_delta_runs()
|
|
162
|
+
used = int(runs.get(key, 0))
|
|
163
|
+
if used >= _DELTA_FREE_LIMIT:
|
|
164
|
+
return False, used, 0
|
|
165
|
+
new_used = used + 1
|
|
166
|
+
runs[key] = new_used
|
|
167
|
+
try:
|
|
168
|
+
_LICENSE_DIR.mkdir(parents=True, exist_ok=True)
|
|
169
|
+
tmp = _DELTA_RUNS_FILE.with_suffix(".tmp")
|
|
170
|
+
tmp.write_text(json.dumps(runs, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
171
|
+
tmp.replace(_DELTA_RUNS_FILE)
|
|
172
|
+
except Exception:
|
|
173
|
+
pass
|
|
174
|
+
return True, new_used, max(0, _DELTA_FREE_LIMIT - new_used)
|
|
175
|
+
|
|
176
|
+
|
|
116
177
|
def _load_license_file() -> Optional[dict]:
|
|
117
178
|
"""Read ~/.sourcecode/license.json. Returns parsed dict or None."""
|
|
118
179
|
try:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=5WWLHrnlJwQHZyPIM9VKQoB5GUOYgv95o6WN75zBV60,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=eMcUIP_tUpij-4lgrtXIU0bvVOFVbKzILsdFPEdGE0E,226810
|
|
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
|
|
@@ -26,7 +26,7 @@ sourcecode/flow_analyzer.py,sha256=dSiuY4w49k29jW_EPXUOND9B5uVbuCA7kjnuHi-pIWA,2
|
|
|
26
26
|
sourcecode/fqn_utils.py,sha256=XLU7zDkNBXz_RZkIUNfpPmp1nekWtqP-fxV92tDV1vg,2158
|
|
27
27
|
sourcecode/git_analyzer.py,sha256=JStxTQXNjBWi_wLdwhsZs9mT-v50cSJIz4Agzn6Kh9I,13362
|
|
28
28
|
sourcecode/graph_analyzer.py,sha256=DHR8fY69oU_Pi4SYaWboX6EoEFrctQKB9dsjpqwGMzw,62403
|
|
29
|
-
sourcecode/license.py,sha256
|
|
29
|
+
sourcecode/license.py,sha256=1nr-uhgtiMUCMH7Ff-tSD8EDXmUNQsNiRiMcIbdCqUY,14940
|
|
30
30
|
sourcecode/mcp_nudge.py,sha256=5ELU_ixzh6uA83NXLOZT8h00OhL53okfQdji3jyKOjg,2917
|
|
31
31
|
sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
|
|
32
32
|
sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
|
|
@@ -93,8 +93,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
93
93
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
94
94
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
95
95
|
sourcecode/telemetry/transport.py,sha256=QSslxIwij8YkRWcVvxykODDrkiN_GAAEu3dUP7KIWeE,1651
|
|
96
|
-
sourcecode-1.35.
|
|
97
|
-
sourcecode-1.35.
|
|
98
|
-
sourcecode-1.35.
|
|
99
|
-
sourcecode-1.35.
|
|
100
|
-
sourcecode-1.35.
|
|
96
|
+
sourcecode-1.35.17.dist-info/METADATA,sha256=yxu3wNqg3zjWzv1AodVb0wv0eQhrmm7dAWzM2LfI_4s,21297
|
|
97
|
+
sourcecode-1.35.17.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
98
|
+
sourcecode-1.35.17.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
99
|
+
sourcecode-1.35.17.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
100
|
+
sourcecode-1.35.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|