sourcecode 1.30.24__py3-none-any.whl → 1.30.25__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.30.24"
3
+ __version__ = "1.30.25"
sourcecode/cli.py CHANGED
@@ -2030,6 +2030,10 @@ def prepare_context_cmd(
2030
2030
  out["runtime_changes"] = output.runtime_changes
2031
2031
  if output.build_changes:
2032
2032
  out["build_changes"] = output.build_changes
2033
+ if output.committed_changes:
2034
+ out["committed_changes"] = output.committed_changes
2035
+ if output.uncommitted_changes:
2036
+ out["uncommitted_changes"] = output.uncommitted_changes
2033
2037
  if output.review_hotspots:
2034
2038
  out["review_hotspots"] = output.review_hotspots
2035
2039
  if output.suggested_review_order:
@@ -2047,6 +2051,27 @@ def prepare_context_cmd(
2047
2051
  "files": output.scope_files,
2048
2052
  "repo_root": output.repo_root or "",
2049
2053
  }
2054
+ # analysis_limiter: consolidate missing graph signals into one field
2055
+ _missing_signals: list[str] = []
2056
+ _dgraph = output.dependency_graph_summary or {}
2057
+ if not _dgraph.get("has_graph_evidence"):
2058
+ _missing_signals.append("dependency_graph")
2059
+ _has_import_ev = any(
2060
+ (rc.get("role") or {}).get("has_annotation_signal") or
2061
+ (rc.get("role") or {}).get("has_symbol_signal")
2062
+ for rc in (output.runtime_changes or [])
2063
+ )
2064
+ if not _has_import_ev:
2065
+ _missing_signals.append("import_graph")
2066
+ if _missing_signals:
2067
+ out["analysis_limiter"] = {"missing_signals": _missing_signals}
2068
+ # classification_confidence: global note when all runtime files are low confidence
2069
+ _all_low = bool(output.runtime_changes) and all(
2070
+ (rc.get("change_effect") or {}).get("epistemic_level") == "INFERRED (LOW CONFIDENCE)"
2071
+ for rc in output.runtime_changes
2072
+ )
2073
+ if _all_low:
2074
+ out["classification_confidence"] = "low"
2050
2075
  if output.limitations:
2051
2076
  out["limitations"] = output.limitations
2052
2077
  if output.symptom:
@@ -171,13 +171,31 @@ def _section_priority_order(out: dict) -> str:
171
171
  lines.append("*No priority order available — diff scope too narrow to rank.*")
172
172
  return "\n".join(lines)
173
173
 
174
- for i, path in enumerate(order[:_MAX_PRIORITY_FILES], 1):
174
+ visible = order[:_MAX_PRIORITY_FILES]
175
+ all_low = visible and all(
176
+ (cls_map.get(p, {}).get("change_effect") or {}).get("epistemic_level")
177
+ == "INFERRED (LOW CONFIDENCE)"
178
+ for p in visible
179
+ )
180
+
181
+ for i, path in enumerate(visible, 1):
175
182
  rc = cls_map.get(path, {})
176
183
  effect = rc.get("change_effect", {})
177
184
  stmt = effect.get("statement", "application source") if isinstance(effect, dict) else "application source"
178
- ep = effect.get("epistemic_level", "STRUCTURAL SIGNAL") if isinstance(effect, dict) else "STRUCTURAL SIGNAL"
179
185
  atype = rc.get("artifact_type", "source")
180
- lines.append(f"{i}. `{_short(path)}` — {_badge(ep)}: {atype} — {stmt}")
186
+ if all_low:
187
+ lines.append(f"{i}. `{_short(path)}` — {atype} — {stmt}")
188
+ else:
189
+ ep = effect.get("epistemic_level", "STRUCTURAL SIGNAL") if isinstance(effect, dict) else "STRUCTURAL SIGNAL"
190
+ lines.append(f"{i}. `{_short(path)}` — {_badge(ep)}: {atype} — {stmt}")
191
+
192
+ if all_low:
193
+ lines.append("")
194
+ lines.append(
195
+ f"*Classification confidence: low for all changed files "
196
+ f"{_badge('INFERRED (LOW CONFIDENCE)')} — "
197
+ "no annotation/import signals found in changed files.*"
198
+ )
181
199
 
182
200
  return "\n".join(lines)
183
201
 
@@ -269,50 +287,26 @@ def _section_signals(out: dict) -> str:
269
287
  def _section_guidance(out: dict) -> str:
270
288
  lines = ["### 5. Review Guidance", ""]
271
289
 
272
- # Inspect first top priority files
273
- order: list[str] = out.get("suggested_review_order", []) or out.get("review_hotspots", [])
274
- runtime: list[dict] = out.get("runtime_changes", [])
275
- cls_map: dict[str, dict] = {rc["path"]: rc for rc in runtime}
276
-
277
- if order:
278
- lines.append("**Inspect first:**")
279
- for path in order[:3]:
280
- rc = cls_map.get(path, {})
281
- effect = rc.get("change_effect", {})
282
- stmt = effect.get("statement", "") if isinstance(effect, dict) else ""
283
- ep = effect.get("epistemic_level", "STRUCTURAL SIGNAL") if isinstance(effect, dict) else "STRUCTURAL SIGNAL"
284
- label = f" — {stmt}" if stmt else ""
285
- lines.append(f"- `{_short(path)}`{label} {_badge(ep)}")
286
- lines.append("")
287
-
288
- # Uncertain — heuristic-only behavioral paths
289
- behavioral: list[dict] = out.get("behavioral_impact", [])
290
- weak_paths = [
291
- bi for bi in behavioral
292
- if bi.get("evidence_level", "none") == "heuristic_only"
293
- ]
294
- if weak_paths:
295
- lines.append("**Uncertain (heuristic-only — verify manually):**")
296
- for bi in weak_paths[:_MAX_UNCERTAIN]:
297
- entry = bi.get("entry_point", "")
298
- affected: list[str] = bi.get("affected_path", [])
299
- path_str = " → ".join(affected) if affected else "unknown"
300
- lines.append(
301
- f"- `{entry}` → {path_str} "
302
- f"{_badge('INFERRED (LOW CONFIDENCE)')}: class reference only, no injection/call proof"
303
- )
304
- lines.append("")
305
-
306
- # Evidence gaps
290
+ # Evidence gaps ONLY — "Inspect first" covered by §3, "Uncertain" covered by §2
307
291
  gaps: list[str] = out.get("gaps", [])
308
292
  cov = out.get("test_coverage_risk", {})
309
293
  test_files: list[str] = cov.get("changed_files_without_tests", [])
310
294
  test_basis = cov.get("basis", "")
311
295
  cov_ep = cov.get("epistemic_level", "INFERRED (LOW CONFIDENCE)")
312
296
 
313
- gap_items: list[str] = list(gaps)
297
+ # Skip graph-absence gaps already captured in analysis_limiter
298
+ missing_signals: set[str] = set(
299
+ (out.get("analysis_limiter") or {}).get("missing_signals", [])
300
+ )
301
+ gap_items: list[str] = [
302
+ g for g in gaps
303
+ if not missing_signals or not any(
304
+ sig.replace("_", " ") in g.lower() for sig in missing_signals
305
+ )
306
+ ]
307
+
314
308
  if test_files:
315
- shown = test_files[:5]
309
+ shown = test_files[:3]
316
310
  omitted = len(test_files) - len(shown)
317
311
  names = ", ".join(f"`{_short(f)}`" for f in shown)
318
312
  suffix = f" +{omitted} more" if omitted else ""
@@ -323,12 +317,19 @@ def _section_guidance(out: dict) -> str:
323
317
  )
324
318
 
325
319
  if gap_items:
326
- lines.append("**Evidence gaps / missing coverage:**")
327
- for g in gap_items:
320
+ lines.append("**Evidence gaps:**")
321
+ for g in gap_items[:3]:
328
322
  lines.append(f"- {g}")
329
323
  else:
330
324
  lines.append("*No evidence gaps detected.*")
331
325
 
326
+ if missing_signals:
327
+ lines.append("")
328
+ lines.append(
329
+ f"{_badge('OMITTED')}: {', '.join(sorted(missing_signals))} — "
330
+ "graph-based impact analysis unavailable"
331
+ )
332
+
332
333
  return "\n".join(lines)
333
334
 
334
335
 
@@ -1271,9 +1271,9 @@ class TaskContextBuilder:
1271
1271
  "evidence": _evidence,
1272
1272
  "change_effect": {
1273
1273
  "statement": _ARTIFACT_CHANGE_EFFECT.get(_f_atype, "application source file (role could not be confirmed)"),
1274
- "classification_method": _f_cls.get("confidence", "low"),
1274
+ "classification_method": _role_basis,
1275
1275
  "epistemic_level": (
1276
- "STRUCTURAL SIGNAL" if _f_cls.get("confidence", "low") == "high"
1276
+ "STRUCTURAL SIGNAL" if _has_code_ev
1277
1277
  else "INFERRED (LOW CONFIDENCE)"
1278
1278
  ),
1279
1279
  },
@@ -2054,10 +2054,18 @@ class TaskContextBuilder:
2054
2054
  if since is None and not committed_files and not uncommitted_files:
2055
2055
  head_ok = _run("git", "rev-parse", "--verify", "HEAD~1")
2056
2056
  if head_ok is not None: # HEAD~1 exists
2057
- head_committed = _run("git", "diff", "--name-only", "--relative", "HEAD~1", "HEAD")
2057
+ head_committed = _run("git", "diff", "--name-only", "--relative", "HEAD~1..HEAD")
2058
2058
  if head_committed:
2059
2059
  committed_files = head_committed
2060
2060
  sources.append(DiffSourceType.HEAD_MINUS_1.value)
2061
+ else:
2062
+ # First commit — no HEAD~1; diff against git empty tree
2063
+ _GIT_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
2064
+ first_committed = _run("git", "diff", "--name-only", "--relative",
2065
+ _GIT_EMPTY_TREE, "HEAD")
2066
+ if first_committed:
2067
+ committed_files = first_committed
2068
+ sources.append("initial_commit")
2061
2069
 
2062
2070
  # ── Drop paths outside self.root ──────────────────────────────────────
2063
2071
  def _drop_outside(lst: list[str]) -> list[str]:
@@ -3280,8 +3288,14 @@ class TaskContextBuilder:
3280
3288
  files = _diff("HEAD~1")
3281
3289
  if files is not None:
3282
3290
  return _make(files or [], "HEAD~1", "head_minus_1_fallback")
3291
+ else:
3292
+ # First commit — no HEAD~1; diff against git empty tree
3293
+ _GIT_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
3294
+ files = _diff(_GIT_EMPTY_TREE)
3295
+ if files is not None:
3296
+ return _make(files or [], _GIT_EMPTY_TREE, "initial_commit_fallback")
3283
3297
 
3284
- # Confirmed no changes: first commit or empty repo
3298
+ # Confirmed no changes: empty repo
3285
3299
  return {
3286
3300
  "files": [],
3287
3301
  "resolved_ref": "HEAD",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.30.24
3
+ Version: 1.30.25
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
- ![Version](https://img.shields.io/badge/version-1.30.24-blue)
224
+ ![Version](https://img.shields.io/badge/version-1.30.25-blue)
225
225
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
226
226
 
227
227
  ---
@@ -257,7 +257,7 @@ pipx install sourcecode
257
257
 
258
258
  ```bash
259
259
  sourcecode version
260
- # sourcecode 1.30.24
260
+ # sourcecode 1.30.25
261
261
  ```
262
262
 
263
263
  ---
@@ -1,10 +1,10 @@
1
- sourcecode/__init__.py,sha256=_G4L9dLoe-xZdbMh8lwWvEdlI0FXQlCazMjnG8FzJ7s,104
1
+ sourcecode/__init__.py,sha256=kJzip46z8Zv1pZNNGqmBkCQorsqmA5F7jeh9VqnPkaY,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
5
5
  sourcecode/ast_extractor.py,sha256=XgrZg2DcWcUm9r87cRG3KGO7IK2TIL_N-CvhSbUmmh4,49901
6
6
  sourcecode/classifier.py,sha256=pYve2J1LqtYssU3lYLMDz18PT-CjN5c18QYE7R_IG1Q,7507
7
- sourcecode/cli.py,sha256=MhXLytfQdGp4L25c6gbhfJyeL5R11ox-wGo-wrO5Ph8,93201
7
+ sourcecode/cli.py,sha256=xNbK_xwsqdhUbAJT4VieJDitGlhMu_O_Y5xzY8yrc9Q,94497
8
8
  sourcecode/code_notes_analyzer.py,sha256=y1MJBnPZHYp4i6cQCXUb9ATIyifS_qMQWjw_8lPkpsU,9215
9
9
  sourcecode/confidence_analyzer.py,sha256=xw_Jv8pAd0wd8t2vvQlorw8Ih0rSF3YCoFS8K-_4aXg,15762
10
10
  sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
@@ -21,8 +21,8 @@ sourcecode/flow_analyzer.py,sha256=dSiuY4w49k29jW_EPXUOND9B5uVbuCA7kjnuHi-pIWA,2
21
21
  sourcecode/git_analyzer.py,sha256=0Gyj-vMpIIN4nfriKXVRouNYBeJ59s6pQDX2Xu9Pq-U,13177
22
22
  sourcecode/graph_analyzer.py,sha256=iUK-7pSV-cvGqqD2hENdYmhnm0wcXFEyK-xnu5ul8OU,62515
23
23
  sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
24
- sourcecode/pr_comment_renderer.py,sha256=k5pCIP6iBNwy5UYPxu47CAq-62j4E9QZZOPyL3trH80,14799
25
- sourcecode/prepare_context.py,sha256=uCMZW2_e4yWolItpv_8YszDNA_R00gSIvy-2wz4jry4,165680
24
+ sourcecode/pr_comment_renderer.py,sha256=smHslxiG14lrytCkq5nFrFu-qTHgA-t-LFYfdrfjz2o,14423
25
+ sourcecode/prepare_context.py,sha256=PlmfECy56vJwPthp-9YwFKHtgaD6XZwreEhyZ7q-G7E,166416
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.24.dist-info/METADATA,sha256=kS8mWyMiWenuH5JumNncBGMFTYmATgv-K15-W9ZlVYI,28956
68
- sourcecode-1.30.24.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
69
- sourcecode-1.30.24.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
70
- sourcecode-1.30.24.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
71
- sourcecode-1.30.24.dist-info/RECORD,,
67
+ sourcecode-1.30.25.dist-info/METADATA,sha256=kPiIROhykJQpgwjLYSkJKtEqag987u3EloszmgQ2HmY,28956
68
+ sourcecode-1.30.25.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
69
+ sourcecode-1.30.25.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
70
+ sourcecode-1.30.25.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
71
+ sourcecode-1.30.25.dist-info/RECORD,,