sourcecode 1.30.22__py3-none-any.whl → 1.30.23__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/prepare_context.py +126 -25
- {sourcecode-1.30.22.dist-info → sourcecode-1.30.23.dist-info}/METADATA +3 -3
- {sourcecode-1.30.22.dist-info → sourcecode-1.30.23.dist-info}/RECORD +7 -7
- {sourcecode-1.30.22.dist-info → sourcecode-1.30.23.dist-info}/WHEEL +0 -0
- {sourcecode-1.30.22.dist-info → sourcecode-1.30.23.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.30.22.dist-info → sourcecode-1.30.23.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/prepare_context.py
CHANGED
|
@@ -25,6 +25,7 @@ class DiffSourceType(str, Enum):
|
|
|
25
25
|
WORKTREE_STAGED = "WORKTREE_STAGED" # git diff --cached
|
|
26
26
|
GIT_SINCE_REF = "GIT_SINCE_REF" # git diff ref HEAD
|
|
27
27
|
GIT_RANGE = "GIT_RANGE" # git diff refA refB
|
|
28
|
+
HEAD_MINUS_1 = "HEAD_MINUS_1" # git diff HEAD~1 HEAD (auto-fallback when tree clean)
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -601,6 +602,83 @@ _FRONTEND_SYMPTOM_MAP: dict[str, list[str]] = {
|
|
|
601
602
|
}
|
|
602
603
|
|
|
603
604
|
|
|
605
|
+
def _build_analysis_scope(
|
|
606
|
+
*,
|
|
607
|
+
task_name: str,
|
|
608
|
+
since: Optional[str],
|
|
609
|
+
delta_baseline: dict,
|
|
610
|
+
pr_scope_source: str,
|
|
611
|
+
pr_uncommitted_files: list,
|
|
612
|
+
) -> dict:
|
|
613
|
+
"""Build analysis_scope metadata from actual git resolution — never hardcoded."""
|
|
614
|
+
if task_name == "review-pr":
|
|
615
|
+
sources = pr_scope_source.split(",") if pr_scope_source else []
|
|
616
|
+
# Derive git_equivalent_command from sources
|
|
617
|
+
cmds: list[str] = []
|
|
618
|
+
if DiffSourceType.GIT_RANGE.value in sources or DiffSourceType.GIT_SINCE_REF.value in sources:
|
|
619
|
+
cmds.append(f"git diff --name-only {since} HEAD")
|
|
620
|
+
if DiffSourceType.HEAD_MINUS_1.value in sources:
|
|
621
|
+
cmds.append("git diff --name-only HEAD~1 HEAD")
|
|
622
|
+
if DiffSourceType.WORKTREE_UNSTAGED.value in sources:
|
|
623
|
+
cmds.append("git diff --name-only")
|
|
624
|
+
if DiffSourceType.WORKTREE_STAGED.value in sources:
|
|
625
|
+
cmds.append("git diff --name-only --cached")
|
|
626
|
+
git_cmd = " + ".join(cmds) if cmds else "git diff --name-only"
|
|
627
|
+
return {
|
|
628
|
+
"sources_used": sources,
|
|
629
|
+
"git_equivalent_command": git_cmd,
|
|
630
|
+
"includes_uncommitted": bool(pr_uncommitted_files),
|
|
631
|
+
"git_resolution": {
|
|
632
|
+
"source_of_truth": "RANGE" if since else (
|
|
633
|
+
"HEAD" if DiffSourceType.HEAD_MINUS_1.value in sources else "WORKTREE"
|
|
634
|
+
),
|
|
635
|
+
"resolved_diff_strategy": pr_scope_source,
|
|
636
|
+
"commit_count_analyzed": 1 if DiffSourceType.HEAD_MINUS_1.value in sources else (
|
|
637
|
+
0 if not since else None
|
|
638
|
+
),
|
|
639
|
+
},
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
# delta
|
|
643
|
+
rpath = delta_baseline.get("resolution_path", "")
|
|
644
|
+
rref = delta_baseline.get("resolved_ref", "HEAD")
|
|
645
|
+
|
|
646
|
+
_COMMITTED_PATHS = {"exact_local_ref", "remote_tracking_ref", "symbolic_ref"}
|
|
647
|
+
if rpath in _COMMITTED_PATHS:
|
|
648
|
+
sources = [DiffSourceType.GIT_SINCE_REF.value]
|
|
649
|
+
git_cmd = f"git diff --name-only {rref} HEAD"
|
|
650
|
+
source_of_truth = "RANGE"
|
|
651
|
+
includes_uncommitted = False
|
|
652
|
+
elif rpath == "head_minus_1_fallback":
|
|
653
|
+
sources = [DiffSourceType.HEAD_MINUS_1.value]
|
|
654
|
+
git_cmd = "git diff --name-only HEAD~1 HEAD"
|
|
655
|
+
source_of_truth = "HEAD"
|
|
656
|
+
includes_uncommitted = False
|
|
657
|
+
elif rpath == "uncommitted_staged":
|
|
658
|
+
sources = [DiffSourceType.WORKTREE_STAGED.value]
|
|
659
|
+
git_cmd = "git diff --name-only --cached"
|
|
660
|
+
source_of_truth = "STAGED"
|
|
661
|
+
includes_uncommitted = True
|
|
662
|
+
else: # uncommitted_unstaged, uncommitted_changes, no_changes_confirmed
|
|
663
|
+
sources = [DiffSourceType.WORKTREE_UNSTAGED.value]
|
|
664
|
+
git_cmd = "git diff --name-only"
|
|
665
|
+
source_of_truth = "WORKTREE"
|
|
666
|
+
includes_uncommitted = True
|
|
667
|
+
|
|
668
|
+
return {
|
|
669
|
+
"sources_used": sources,
|
|
670
|
+
"git_equivalent_command": git_cmd,
|
|
671
|
+
"includes_uncommitted": includes_uncommitted,
|
|
672
|
+
"git_resolution": {
|
|
673
|
+
"source_of_truth": source_of_truth,
|
|
674
|
+
"resolved_diff_strategy": rpath or "unknown",
|
|
675
|
+
"commit_count_analyzed": 1 if rpath == "head_minus_1_fallback" else (
|
|
676
|
+
0 if rpath == "no_changes_confirmed" else None
|
|
677
|
+
),
|
|
678
|
+
},
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
|
|
604
682
|
class TaskContextBuilder:
|
|
605
683
|
def __init__(self, root: Path) -> None:
|
|
606
684
|
self.root = root
|
|
@@ -870,9 +948,11 @@ class TaskContextBuilder:
|
|
|
870
948
|
# For delta task, relevant_files must rank only files changed in the
|
|
871
949
|
# specified git range, not the full repo by generic entrypoint scoring.
|
|
872
950
|
_delta_files: Optional[set[str]] = None
|
|
951
|
+
_delta_baseline: dict = {} # resolution metadata threaded into analysis_scope
|
|
873
952
|
if task_name == "delta":
|
|
874
|
-
|
|
875
|
-
|
|
953
|
+
_baseline = self._resolve_git_baseline(since=since)
|
|
954
|
+
_delta_baseline = _baseline
|
|
955
|
+
if _baseline["error"]:
|
|
876
956
|
# Explicit --since ref couldn't be resolved — hard error, no fallback
|
|
877
957
|
_avail_branches, _suggested = self._get_available_refs(since or "")
|
|
878
958
|
_hints: list[str] = []
|
|
@@ -899,8 +979,11 @@ class TaskContextBuilder:
|
|
|
899
979
|
error_hints=_hints,
|
|
900
980
|
gaps=[f"Cannot compute delta: git ref '{since}' not found."] + _hints,
|
|
901
981
|
ci_decision="git_ref_error",
|
|
982
|
+
resolution_path=_baseline.get("resolution_path"),
|
|
983
|
+
diff_validation_status=_baseline.get("diff_validation_status"),
|
|
902
984
|
)
|
|
903
|
-
|
|
985
|
+
_delta_raw = _baseline["files"]
|
|
986
|
+
if _delta_raw:
|
|
904
987
|
_delta_files = set(_delta_raw)
|
|
905
988
|
|
|
906
989
|
# ── 5d. review-pr: set _delta_files from pre-resolved git scope ──────────
|
|
@@ -1515,7 +1598,11 @@ class TaskContextBuilder:
|
|
|
1515
1598
|
changed_files: list[str] = []
|
|
1516
1599
|
affected_entry_points: list[str] = []
|
|
1517
1600
|
if task_name in ("delta", "review-pr"):
|
|
1518
|
-
|
|
1601
|
+
# For delta: _delta_files already resolved via _resolve_git_baseline — no second git call.
|
|
1602
|
+
# For review-pr: _get_git_changed_files fallback still valid as last resort.
|
|
1603
|
+
changed_files = sorted(_delta_files) if _delta_files else (
|
|
1604
|
+
[] if task_name == "delta" else (self._get_git_changed_files(since=since) or [])
|
|
1605
|
+
)
|
|
1519
1606
|
_ep_set = {ep.path for ep in entry_points}
|
|
1520
1607
|
# include framework-detected entry points AND files classified as
|
|
1521
1608
|
# entrypoint/controller/security by artifact taxonomy
|
|
@@ -1582,21 +1669,16 @@ class TaskContextBuilder:
|
|
|
1582
1669
|
committed_changes=_pr_committed_changes,
|
|
1583
1670
|
uncommitted_changes=_pr_uncommitted_changes,
|
|
1584
1671
|
# transparency: explicit diff scope
|
|
1585
|
-
analysis_scope=
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
"includes_uncommitted": bool(
|
|
1596
|
-
(task_name == "review-pr" and _pr_uncommitted_files) or
|
|
1597
|
-
(task_name == "delta" and not since)
|
|
1598
|
-
),
|
|
1599
|
-
} if task_name in ("delta", "review-pr") else {},
|
|
1672
|
+
analysis_scope=_build_analysis_scope(
|
|
1673
|
+
task_name=task_name,
|
|
1674
|
+
since=since,
|
|
1675
|
+
delta_baseline=_delta_baseline,
|
|
1676
|
+
pr_scope_source=_pr_scope_source,
|
|
1677
|
+
pr_uncommitted_files=_pr_uncommitted_files,
|
|
1678
|
+
) if task_name in ("delta", "review-pr") else {},
|
|
1679
|
+
resolved_since_ref=_delta_baseline.get("resolved_ref") if task_name == "delta" else None,
|
|
1680
|
+
resolution_path=_delta_baseline.get("resolution_path") if task_name == "delta" else None,
|
|
1681
|
+
diff_validation_status=_delta_baseline.get("diff_validation_status") if task_name == "delta" else None,
|
|
1600
1682
|
)
|
|
1601
1683
|
|
|
1602
1684
|
def render_prompt(self, output: TaskOutput) -> str:
|
|
@@ -1962,6 +2044,17 @@ class TaskContextBuilder:
|
|
|
1962
2044
|
if DiffSourceType.WORKTREE_STAGED.value not in sources:
|
|
1963
2045
|
sources.append(DiffSourceType.WORKTREE_STAGED.value)
|
|
1964
2046
|
|
|
2047
|
+
# ── HEAD~1 fallback — working tree clean, surface last commit ────────
|
|
2048
|
+
# Without --since: if no unstaged/staged changes exist, fall back to the
|
|
2049
|
+
# last committed diff so a clean tree never silently returns no_changes.
|
|
2050
|
+
if since is None and not committed_files and not uncommitted_files:
|
|
2051
|
+
head_ok = _run("git", "rev-parse", "--verify", "HEAD~1")
|
|
2052
|
+
if head_ok is not None: # HEAD~1 exists
|
|
2053
|
+
head_committed = _run("git", "diff", "--name-only", "--relative", "HEAD~1", "HEAD")
|
|
2054
|
+
if head_committed:
|
|
2055
|
+
committed_files = head_committed
|
|
2056
|
+
sources.append(DiffSourceType.HEAD_MINUS_1.value)
|
|
2057
|
+
|
|
1965
2058
|
# ── Drop paths outside self.root ──────────────────────────────────────
|
|
1966
2059
|
def _drop_outside(lst: list[str]) -> list[str]:
|
|
1967
2060
|
return [f for f in lst if not f.startswith("../") and not f.startswith("..\\")]
|
|
@@ -3164,25 +3257,33 @@ class TaskContextBuilder:
|
|
|
3164
3257
|
}
|
|
3165
3258
|
|
|
3166
3259
|
else:
|
|
3167
|
-
# No since:
|
|
3260
|
+
# No since: unstaged → staged → HEAD~1 — never error, clean tree is valid
|
|
3168
3261
|
ok, out = _run("diff", "--name-only", "--relative", timeout=10)
|
|
3169
3262
|
if ok:
|
|
3170
3263
|
files = [line.strip() for line in out.splitlines() if line.strip()]
|
|
3171
3264
|
if files:
|
|
3172
|
-
return _make(files, "HEAD", "
|
|
3265
|
+
return _make(files, "HEAD", "uncommitted_unstaged")
|
|
3266
|
+
|
|
3267
|
+
# Staged (committed to index but not yet to history)
|
|
3268
|
+
ok, out = _run("diff", "--name-only", "--cached", "--relative", timeout=10)
|
|
3269
|
+
if ok:
|
|
3270
|
+
files = [line.strip() for line in out.splitlines() if line.strip()]
|
|
3271
|
+
if files:
|
|
3272
|
+
return _make(files, "HEAD", "uncommitted_staged")
|
|
3173
3273
|
|
|
3174
|
-
# HEAD~1 fallback
|
|
3274
|
+
# HEAD~1 fallback — working tree clean, surface last commit
|
|
3175
3275
|
if _verify("HEAD~1"):
|
|
3176
3276
|
files = _diff("HEAD~1")
|
|
3177
3277
|
if files is not None:
|
|
3178
3278
|
return _make(files or [], "HEAD~1", "head_minus_1_fallback")
|
|
3179
3279
|
|
|
3280
|
+
# Confirmed no changes: first commit or empty repo
|
|
3180
3281
|
return {
|
|
3181
3282
|
"files": [],
|
|
3182
3283
|
"resolved_ref": "HEAD",
|
|
3183
|
-
"resolution_path": "
|
|
3184
|
-
"diff_validation_status": "
|
|
3185
|
-
"error":
|
|
3284
|
+
"resolution_path": "no_changes_confirmed",
|
|
3285
|
+
"diff_validation_status": "valid_empty",
|
|
3286
|
+
"error": False,
|
|
3186
3287
|
}
|
|
3187
3288
|
|
|
3188
3289
|
def _get_uncommitted_changed_files(self) -> list[str]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.30.
|
|
3
|
+
Version: 1.30.23
|
|
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.23
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
---
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=4VsjXZcGaVFIDFx4kI8Hlzw0_vN-CPBmQkBXMb29s-I,104
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=RTNExwWPXzjgLaRueT7UuxkPj5ZEToWjGbx1j0LSZ9E,10250
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=MyBa0Hf5HmkudZQDLKrjcWDKETXETXl0mQX1swtTwAA,39091
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
|
|
@@ -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=k5pCIP6iBNwy5UYPxu47CAq-62j4E9QZZOPyL3trH80,14799
|
|
25
|
-
sourcecode/prepare_context.py,sha256=
|
|
25
|
+
sourcecode/prepare_context.py,sha256=biZaw1T_D5cQWSUnWBQmdLLdhe-7HClkHtHJgBvqFDs,165387
|
|
26
26
|
sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
|
|
27
27
|
sourcecode/ranking_engine.py,sha256=virVglafZufioHpZpwktjMvUiL0TZELWQCQnQNV8dFo,9360
|
|
28
28
|
sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
|
|
@@ -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.23.dist-info/METADATA,sha256=K9iUA2PpGOnHY1vF9hupWySK0WzXVyvweT4TS_XIYms,28956
|
|
68
|
+
sourcecode-1.30.23.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
69
|
+
sourcecode-1.30.23.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
70
|
+
sourcecode-1.30.23.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
71
|
+
sourcecode-1.30.23.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|