sourcecode 1.30.29__py3-none-any.whl → 1.30.30__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 +41 -4
- sourcecode/prepare_context.py +24 -1
- sourcecode/serializer.py +24 -2
- {sourcecode-1.30.29.dist-info → sourcecode-1.30.30.dist-info}/METADATA +3 -3
- {sourcecode-1.30.29.dist-info → sourcecode-1.30.30.dist-info}/RECORD +9 -9
- {sourcecode-1.30.29.dist-info → sourcecode-1.30.30.dist-info}/WHEEL +0 -0
- {sourcecode-1.30.29.dist-info → sourcecode-1.30.30.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.30.29.dist-info → sourcecode-1.30.30.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -188,6 +188,7 @@ _OPTIONS_WITH_VALUE: frozenset[str] = frozenset({
|
|
|
188
188
|
"--rank-by",
|
|
189
189
|
"--symbol",
|
|
190
190
|
"--max-importers",
|
|
191
|
+
"--exclude",
|
|
191
192
|
})
|
|
192
193
|
|
|
193
194
|
|
|
@@ -851,10 +852,18 @@ def main(
|
|
|
851
852
|
_copy_to_clipboard(_cache_hit_content)
|
|
852
853
|
return
|
|
853
854
|
|
|
854
|
-
# BUG-2: parse --exclude into extra_excludes frozenset
|
|
855
855
|
_extra_excludes: Optional[frozenset[str]] = None
|
|
856
856
|
if exclude:
|
|
857
857
|
_extra_excludes = frozenset(e.strip() for e in exclude.split(",") if e.strip())
|
|
858
|
+
# IMP-2: warn if the exclude value looks like it was swallowed as a path
|
|
859
|
+
# (BUG-2 symptom in older versions: --exclude value consumed as repo path).
|
|
860
|
+
import sys as _sys_warn
|
|
861
|
+
if len(_extra_excludes) == 1 and Path(list(_extra_excludes)[0]).is_dir():
|
|
862
|
+
_sys_warn.stderr.write(
|
|
863
|
+
f"[sourcecode] Warning: --exclude value '{list(_extra_excludes)[0]}' is a directory path. "
|
|
864
|
+
"If this was meant as a pattern, use --exclude=pattern or --exclude pattern (both are supported).\n"
|
|
865
|
+
)
|
|
866
|
+
_sys_warn.stderr.flush()
|
|
858
867
|
|
|
859
868
|
_progress = Progress()
|
|
860
869
|
_progress.start("scanning files")
|
|
@@ -1807,6 +1816,11 @@ def prepare_context_cmd(
|
|
|
1807
1816
|
"--fast",
|
|
1808
1817
|
help="Skip deep analysis (content search, test gap discovery, code annotations). Uses manifest/metadata only. Target: < 6 s.",
|
|
1809
1818
|
),
|
|
1819
|
+
include_config: bool = typer.Option(
|
|
1820
|
+
False,
|
|
1821
|
+
"--include-config",
|
|
1822
|
+
help="(generate-tests) Include tooling config files (*.conf.js, .eslintrc*, etc.) in test_gaps. Excluded by default.",
|
|
1823
|
+
),
|
|
1810
1824
|
) -> None:
|
|
1811
1825
|
"""Task-specific context for AI coding agents.
|
|
1812
1826
|
|
|
@@ -1888,7 +1902,7 @@ def prepare_context_cmd(
|
|
|
1888
1902
|
_sys.stderr.flush()
|
|
1889
1903
|
_t0 = _time.perf_counter()
|
|
1890
1904
|
try:
|
|
1891
|
-
output = builder.build(task, since=since, symptom=symptom, fast=fast)
|
|
1905
|
+
output = builder.build(task, since=since, symptom=symptom, fast=fast, include_config=include_config)
|
|
1892
1906
|
finally:
|
|
1893
1907
|
_progress.finish()
|
|
1894
1908
|
_t_total = (_time.perf_counter() - _t0) * 1000
|
|
@@ -2359,8 +2373,23 @@ def repo_ir_cmd(
|
|
|
2359
2373
|
err=True,
|
|
2360
2374
|
)
|
|
2361
2375
|
else:
|
|
2362
|
-
|
|
2363
|
-
|
|
2376
|
+
try:
|
|
2377
|
+
_sys.stdout.buffer.write(output.encode("utf-8"))
|
|
2378
|
+
_sys.stdout.buffer.write(b"\n")
|
|
2379
|
+
_sys.stdout.buffer.flush()
|
|
2380
|
+
except UnicodeEncodeError as _ue:
|
|
2381
|
+
# IMP-2: emit workaround before re-raising so the user knows what to do.
|
|
2382
|
+
_sys.stderr.write(
|
|
2383
|
+
f"[sourcecode] UnicodeEncodeError on stdout ({_ue.encoding}): "
|
|
2384
|
+
"your console codec cannot encode this output.\n"
|
|
2385
|
+
"Workaround: sourcecode repo-ir --output ir.json\n"
|
|
2386
|
+
)
|
|
2387
|
+
_sys.stderr.flush()
|
|
2388
|
+
raise
|
|
2389
|
+
except AttributeError:
|
|
2390
|
+
# Fallback for wrapped stdout without buffer (e.g. some test harnesses)
|
|
2391
|
+
_sys.stdout.write(output)
|
|
2392
|
+
_sys.stdout.write("\n")
|
|
2364
2393
|
|
|
2365
2394
|
|
|
2366
2395
|
# ── version ───────────────────────────────────────────────────────────────────
|
|
@@ -2413,5 +2442,13 @@ def main_entry() -> None:
|
|
|
2413
2442
|
can consume them as positional arguments (which would prevent subcommand
|
|
2414
2443
|
routing for tokens like 'version' or 'config').
|
|
2415
2444
|
"""
|
|
2445
|
+
import sys as _sys
|
|
2446
|
+
# Force UTF-8 on stdout so Unicode characters (arrows, etc.) survive on
|
|
2447
|
+
# Windows where the default console codec is cp1252 (BUG-1).
|
|
2448
|
+
if hasattr(_sys.stdout, "reconfigure"):
|
|
2449
|
+
try:
|
|
2450
|
+
_sys.stdout.reconfigure(encoding="utf-8")
|
|
2451
|
+
except Exception:
|
|
2452
|
+
pass
|
|
2416
2453
|
_preprocess_argv()
|
|
2417
2454
|
app()
|
sourcecode/prepare_context.py
CHANGED
|
@@ -712,7 +712,7 @@ class TaskContextBuilder:
|
|
|
712
712
|
def __init__(self, root: Path) -> None:
|
|
713
713
|
self.root = root
|
|
714
714
|
|
|
715
|
-
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False) -> TaskOutput:
|
|
715
|
+
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False, include_config: bool = False) -> TaskOutput:
|
|
716
716
|
if task_name not in TASKS:
|
|
717
717
|
raise ValueError(
|
|
718
718
|
f"Unknown task '{task_name}'. Available: {', '.join(TASKS)}"
|
|
@@ -788,6 +788,14 @@ class TaskContextBuilder:
|
|
|
788
788
|
# for behavioral_impact reverse lookups without scanning the whole repo).
|
|
789
789
|
file_tree: dict = {}
|
|
790
790
|
all_paths = self._expand_scope_for_analysis(_pr_scope_files or [])
|
|
791
|
+
elif fast and task_name == "onboard":
|
|
792
|
+
# Onboard fast: always use shallow scan so manifests and entry points
|
|
793
|
+
# are discoverable — git-changed-only mode would return only dirty files
|
|
794
|
+
# (e.g. .idea/vcs.xml) which yields no useful entry points (BUG-3).
|
|
795
|
+
scanner = AdaptiveScanner(self.root, base_depth=2)
|
|
796
|
+
file_tree = scanner.scan_tree()
|
|
797
|
+
manifests = scanner.find_manifests()
|
|
798
|
+
all_paths = [p.replace("\\", "/") for p in flatten_file_tree(file_tree)]
|
|
791
799
|
elif fast and _count_files_bounded(self.root) > MAX_FILES_FAST:
|
|
792
800
|
# Fast mode on large repo: git-index-only — only scan git-changed files.
|
|
793
801
|
# Skips full AdaptiveScanner traversal which takes 35s+ on 7k+ file repos.
|
|
@@ -1652,11 +1660,26 @@ class TaskContextBuilder:
|
|
|
1652
1660
|
# Python/JS: test_foo / foo_test
|
|
1653
1661
|
return stem.removeprefix("test_").removesuffix("_test")
|
|
1654
1662
|
|
|
1663
|
+
# Patterns excluded from test_gaps by default (IMP-1): tooling config
|
|
1664
|
+
# files have no business logic to test. --include-config overrides.
|
|
1665
|
+
_CONFIG_EXCLUDE_PATTERNS = (
|
|
1666
|
+
".eslintrc", ".prettierrc", "eslint.config",
|
|
1667
|
+
"karma.conf", "jest.config", "babel.config",
|
|
1668
|
+
"webpack.config", "vite.config", "rollup.config",
|
|
1669
|
+
"tsconfig", "angular.json", ".claude/",
|
|
1670
|
+
)
|
|
1671
|
+
|
|
1672
|
+
def _is_config_file(p: str) -> bool:
|
|
1673
|
+
name = Path(p).name.lower()
|
|
1674
|
+
norm = p.replace("\\", "/")
|
|
1675
|
+
return any(pat in name or pat in norm for pat in _CONFIG_EXCLUDE_PATTERNS)
|
|
1676
|
+
|
|
1655
1677
|
test_stems = {_normalize_test_stem(Path(p).stem) for p in test_set}
|
|
1656
1678
|
untested = [
|
|
1657
1679
|
p for p in source_set
|
|
1658
1680
|
if Path(p).stem not in test_stems
|
|
1659
1681
|
and not any(pen in p for pen in spec.ranking_penalties)
|
|
1682
|
+
and (include_config or not _is_config_file(p))
|
|
1660
1683
|
]
|
|
1661
1684
|
untested.sort(key=lambda p: (len(p.split("/")), p))
|
|
1662
1685
|
test_gaps = untested[:15]
|
sourcecode/serializer.py
CHANGED
|
@@ -1643,7 +1643,23 @@ def _angular_analysis(sm: "SourceMap") -> "Optional[dict[str, Any]]":
|
|
|
1643
1643
|
continue
|
|
1644
1644
|
component_count += content.count("@Component(")
|
|
1645
1645
|
service_count += content.count("@Injectable(")
|
|
1646
|
-
|
|
1646
|
+
# Count lazy route patterns: `loadChildren:` (property syntax used in route
|
|
1647
|
+
# configs) and `loadComponent:` (standalone component lazy loading). The old
|
|
1648
|
+
# `loadChildren(` form counted zero because Angular uses property syntax, not
|
|
1649
|
+
# a function call (BUG-5).
|
|
1650
|
+
fname_lower = rel.replace("\\", "/").split("/")[-1].lower()
|
|
1651
|
+
_is_routing_file = (
|
|
1652
|
+
"routing" in fname_lower
|
|
1653
|
+
or fname_lower in ("app.routes.ts", "app-routing.module.ts")
|
|
1654
|
+
or fname_lower.endswith(".routes.ts")
|
|
1655
|
+
)
|
|
1656
|
+
lazy_routes_count += content.count("loadChildren:")
|
|
1657
|
+
lazy_routes_count += content.count("loadComponent:")
|
|
1658
|
+
if _is_routing_file:
|
|
1659
|
+
# Also count standalone dynamic imports that aren't already caught above
|
|
1660
|
+
_lc_imports = content.count("import(") - content.count("loadChildren:") - content.count("loadComponent:")
|
|
1661
|
+
if _lc_imports > 0:
|
|
1662
|
+
lazy_routes_count += _lc_imports
|
|
1647
1663
|
akita_stores += content.count("@StoreConfig(")
|
|
1648
1664
|
if not standalone_components and "bootstrapApplication(" in content:
|
|
1649
1665
|
standalone_components = True
|
|
@@ -1661,7 +1677,13 @@ def _angular_analysis(sm: "SourceMap") -> "Optional[dict[str, Any]]":
|
|
|
1661
1677
|
if pkg_json.exists():
|
|
1662
1678
|
try:
|
|
1663
1679
|
pkg = _json.loads(pkg_json.read_text(encoding="utf-8", errors="replace"))
|
|
1664
|
-
|
|
1680
|
+
# Use `or {}` so explicit `null` values in package.json don't
|
|
1681
|
+
# raise TypeError when unpacking (BUG-4).
|
|
1682
|
+
deps = {
|
|
1683
|
+
**(pkg.get("dependencies") or {}),
|
|
1684
|
+
**(pkg.get("devDependencies") or {}),
|
|
1685
|
+
**(pkg.get("peerDependencies") or {}),
|
|
1686
|
+
}
|
|
1665
1687
|
av = deps.get("@angular/core")
|
|
1666
1688
|
if av:
|
|
1667
1689
|
angular_version = av.lstrip("^~>=")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.30.
|
|
3
|
+
Version: 1.30.30
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -221,7 +221,7 @@ Description-Content-Type: text/markdown
|
|
|
221
221
|
|
|
222
222
|
**Deterministic, behavior-aware codebase context for AI agents and PR review.**
|
|
223
223
|
|
|
224
|
-

|
|
225
225
|

|
|
226
226
|
|
|
227
227
|
---
|
|
@@ -257,7 +257,7 @@ pipx install sourcecode
|
|
|
257
257
|
|
|
258
258
|
```bash
|
|
259
259
|
sourcecode version
|
|
260
|
-
# sourcecode 1.30.
|
|
260
|
+
# sourcecode 1.30.30
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
---
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=ANLn7vd3QuDTWulbelwm9eIp93h5ZEZKi-nKm911_so,104
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=MyBa0Hf5HmkudZQDLKrjcWDKETXETXl0mQX1swtTwAA,39091
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
|
|
5
5
|
sourcecode/ast_extractor.py,sha256=XgrZg2DcWcUm9r87cRG3KGO7IK2TIL_N-CvhSbUmmh4,49901
|
|
6
6
|
sourcecode/classifier.py,sha256=-0t0HLc9L9UleMLfclfLM3AXhBjUb_AYyBPDbvgWtac,7755
|
|
7
|
-
sourcecode/cli.py,sha256=
|
|
7
|
+
sourcecode/cli.py,sha256=_WsbkJWvI_xv4aSh3mdPvTbh-eKGV5DZbPj2ZFuA2GI,100189
|
|
8
8
|
sourcecode/code_notes_analyzer.py,sha256=y1MJBnPZHYp4i6cQCXUb9ATIyifS_qMQWjw_8lPkpsU,9215
|
|
9
9
|
sourcecode/confidence_analyzer.py,sha256=H9VHYRzZhqMFlSCZffjtsMUGYLnDvrq1g5FjzyQ1hxE,16381
|
|
10
10
|
sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
|
|
@@ -22,7 +22,7 @@ sourcecode/git_analyzer.py,sha256=0Gyj-vMpIIN4nfriKXVRouNYBeJ59s6pQDX2Xu9Pq-U,13
|
|
|
22
22
|
sourcecode/graph_analyzer.py,sha256=iUK-7pSV-cvGqqD2hENdYmhnm0wcXFEyK-xnu5ul8OU,62515
|
|
23
23
|
sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
|
|
24
24
|
sourcecode/pr_comment_renderer.py,sha256=smHslxiG14lrytCkq5nFrFu-qTHgA-t-LFYfdrfjz2o,14423
|
|
25
|
-
sourcecode/prepare_context.py,sha256=
|
|
25
|
+
sourcecode/prepare_context.py,sha256=QNCl8uKk9PQpgXxPHBNSDXXkc1s2wTZwU6H0REC5Qms,173487
|
|
26
26
|
sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
|
|
27
27
|
sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,12970
|
|
28
28
|
sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
|
|
@@ -33,7 +33,7 @@ sourcecode/runtime_classifier.py,sha256=zWX3r3HCKHc-qtIobErOa8aKMmaoPYREtJKvPcBG
|
|
|
33
33
|
sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
|
|
34
34
|
sourcecode/schema.py,sha256=fj3BZ3IcnNV4j21BFIEvz8Qnw_vZoqIbzzRg-qQ-nd0,24530
|
|
35
35
|
sourcecode/semantic_analyzer.py,sha256=12TwXYkYbDcBdu0heX_EmfPM2EkO8a_r5osf0SaeQbs,88956
|
|
36
|
-
sourcecode/serializer.py,sha256=
|
|
36
|
+
sourcecode/serializer.py,sha256=FM4xklb9Ywg9KNdNpo8QXR50izuml5FkbeQgL2uS1HY,111611
|
|
37
37
|
sourcecode/summarizer.py,sha256=lPlKhMh28nueXkPo2xKeD3DUFYVGRlJMIdY-8TSM-ls,17486
|
|
38
38
|
sourcecode/tree_utils.py,sha256=8GAkIfQAsvtEudIeW1l4ooH_oRtrWR8cpJQJsEa_Pfw,2093
|
|
39
39
|
sourcecode/workspace.py,sha256=X_6NmNnitvT3_38V-JDChydo_sR68s249hLFlrQskU0,8271
|
|
@@ -64,8 +64,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
64
64
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
65
65
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
66
66
|
sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
|
|
67
|
-
sourcecode-1.30.
|
|
68
|
-
sourcecode-1.30.
|
|
69
|
-
sourcecode-1.30.
|
|
70
|
-
sourcecode-1.30.
|
|
71
|
-
sourcecode-1.30.
|
|
67
|
+
sourcecode-1.30.30.dist-info/METADATA,sha256=TRgm7Qpvohc3ofAWDRDEfmcF2lCQ3b-dqvEAn-Rd6qw,28956
|
|
68
|
+
sourcecode-1.30.30.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
69
|
+
sourcecode-1.30.30.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
70
|
+
sourcecode-1.30.30.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
71
|
+
sourcecode-1.30.30.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|