sourcecode 1.30.27__py3-none-any.whl → 1.30.28__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.27"
3
+ __version__ = "1.30.28"
sourcecode/cli.py CHANGED
@@ -424,7 +424,7 @@ def main(
424
424
  no_redact: bool = typer.Option(
425
425
  False,
426
426
  "--no-redact",
427
- help="Disable secret redaction. Use with caution output may contain sensitive values.",
427
+ help="Disable secret redaction of output strings. Note: env var values from the OS are never included in output regardless of this flag (security policy).",
428
428
  ),
429
429
  version: Optional[bool] = typer.Option(
430
430
  None,
@@ -437,7 +437,7 @@ def main(
437
437
  depth: int = typer.Option(
438
438
  4,
439
439
  "--depth",
440
- help="File tree traversal depth (default: 4). Java/Maven projects auto-adjust to 12.",
440
+ help="File tree traversal depth (default: 4). Java/Maven projects auto-adjust to a minimum of 12; values below 12 have no effect on Java projects.",
441
441
  min=1,
442
442
  max=20,
443
443
  ),
@@ -1734,6 +1734,11 @@ def prepare_context_cmd(
1734
1734
  help="Emit per-phase timing to stderr (git scan ms, symptom scoring ms, total ms)",
1735
1735
  hidden=True,
1736
1736
  ),
1737
+ fast: bool = typer.Option(
1738
+ False,
1739
+ "--fast",
1740
+ help="Skip deep analysis (content search, test gap discovery, code annotations). Uses manifest/metadata only. Target: < 6 s.",
1741
+ ),
1737
1742
  ) -> None:
1738
1743
  """Task-specific context for AI coding agents.
1739
1744
 
@@ -1808,9 +1813,14 @@ def prepare_context_cmd(
1808
1813
  if since:
1809
1814
  _phase += f" since {since}"
1810
1815
  _progress.start(_phase)
1816
+ if not fast:
1817
+ import sys as _sys
1818
+ if _sys.stderr.isatty():
1819
+ _sys.stderr.write(f"Analyzing ({task})... (deep scan may take 15–35 s for large codebases)\n")
1820
+ _sys.stderr.flush()
1811
1821
  _t0 = _time.perf_counter()
1812
1822
  try:
1813
- output = builder.build(task, since=since, symptom=symptom)
1823
+ output = builder.build(task, since=since, symptom=symptom, fast=fast)
1814
1824
  finally:
1815
1825
  _progress.finish()
1816
1826
  _t_total = (_time.perf_counter() - _t0) * 1000
@@ -1960,8 +1970,8 @@ def prepare_context_cmd(
1960
1970
  _sys.stdout.buffer.write(b"\n")
1961
1971
  _sys.stdout.buffer.flush()
1962
1972
  if copy:
1963
- _copy_to_clipboard(_nc_json)
1964
- typer.echo("✓ copied to clipboard", err=True)
1973
+ if _copy_to_clipboard(_nc_json):
1974
+ typer.echo("✓ copied to clipboard", err=True)
1965
1975
  raise typer.Exit()
1966
1976
  if output.ci_decision:
1967
1977
  out["ci_decision"] = output.ci_decision
@@ -2082,6 +2092,10 @@ def prepare_context_cmd(
2082
2092
  out["symptom_note"] = output.symptom_note
2083
2093
  if output.symptom_explain:
2084
2094
  out["symptom_explain"] = output.symptom_explain
2095
+ if getattr(output, "symptom_hint", None):
2096
+ out["symptom_hint"] = output.symptom_hint
2097
+ if getattr(output, "warnings", None):
2098
+ out["warnings"] = output.warnings
2085
2099
  if llm_prompt:
2086
2100
  out["llm_prompt"] = builder.render_prompt(output)
2087
2101
 
@@ -335,6 +335,7 @@ class TaskOutput:
335
335
  related_notes: list[dict] = field(default_factory=list) # fix-bug + symptom only
336
336
  symptom_note: Optional[str] = None # fix-bug: cross-layer synonym note
337
337
  symptom_explain: Optional[dict] = None # fix-bug: structured evidence breakdown
338
+ symptom_hint: Optional[str] = None # fix-bug: redirect hint when term not found in this module
338
339
  # delta-specific impact fields
339
340
  impact_summary: Optional[str] = None
340
341
  affected_modules: list[str] = field(default_factory=list)
@@ -348,6 +349,7 @@ class TaskOutput:
348
349
  error_code: Optional[str] = None
349
350
  error_message: Optional[str] = None
350
351
  error_hints: list[str] = field(default_factory=list)
352
+ warnings: list[dict] = field(default_factory=list) # structured warnings (REF_NOT_FOUND, etc.)
351
353
  # CI decision state machine — machine-decidable signal
352
354
  ci_decision: Optional[str] = None # "no_changes" | "analysis_success" | "git_ref_error" | "no_git_repo"
353
355
  # git baseline resolution metadata
@@ -683,7 +685,7 @@ class TaskContextBuilder:
683
685
  def __init__(self, root: Path) -> None:
684
686
  self.root = root
685
687
 
686
- def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None) -> TaskOutput:
688
+ def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False) -> TaskOutput:
687
689
  if task_name not in TASKS:
688
690
  raise ValueError(
689
691
  f"Unknown task '{task_name}'. Available: {', '.join(TASKS)}"
@@ -880,7 +882,7 @@ class TaskContextBuilder:
880
882
  improvement_opportunities: list[str] = []
881
883
  cn_notes_for_ranking: list = []
882
884
 
883
- if spec.enable_code_notes:
885
+ if spec.enable_code_notes and not fast:
884
886
  from dataclasses import asdict
885
887
  from sourcecode.code_notes_analyzer import CodeNotesAnalyzer
886
888
 
@@ -1317,6 +1319,7 @@ class TaskContextBuilder:
1317
1319
  related_notes: list[dict] = []
1318
1320
  symptom_note: Optional[str] = None
1319
1321
  symptom_explain: Optional[dict] = None
1322
+ symptom_hint: Optional[str] = None
1320
1323
  if task_name == "fix-bug" and symptom:
1321
1324
  import re as _re
1322
1325
  _camel_expanded = _re.sub(r'([a-z])([A-Z])', r'\1 \2', symptom)
@@ -1537,9 +1540,30 @@ class TaskContextBuilder:
1537
1540
  ),
1538
1541
  }
1539
1542
 
1543
+ # BUG #4: LOW confidence + 0 content matches → clear suspected_areas,
1544
+ # emit actionable redirect instead of unrelated files.
1545
+ if _sx_confidence == "LOW" and not _sx_content:
1546
+ suspected_areas = []
1547
+ _is_fe_term = any(kw in _FRONTEND_SYMPTOM_MAP for kw in symptom_keywords)
1548
+ _root_name = self.root.name
1549
+ if _is_fe_term:
1550
+ _fe_redirect = (
1551
+ f"Term {symptom!r} not found in sources under {_root_name!r}. "
1552
+ f"This appears to be a frontend symptom. "
1553
+ f"Try: prepare-context fix-bug . --symptom {symptom!r} "
1554
+ f"(monorepo root) or target a frontend sub-project directly."
1555
+ )
1556
+ else:
1557
+ _fe_redirect = (
1558
+ f"Term {symptom!r} not found in sources under {_root_name!r}. "
1559
+ f"Verify the spelling or try a related term. "
1560
+ f"If this is a frontend symptom, run against the frontend sub-project."
1561
+ )
1562
+ symptom_hint = _fe_redirect
1563
+
1540
1564
  # ── 7. Test gaps (generate-tests only) ────────────────────────────
1541
1565
  test_gaps: list[str] = []
1542
- if task_name == "generate-tests":
1566
+ if task_name == "generate-tests" and not fast:
1543
1567
  def _normalize_test_stem(stem: str) -> str:
1544
1568
  # Java: FooTest / FooTests → Foo; TestFoo → Foo
1545
1569
  if stem.endswith("Tests"):
@@ -1683,6 +1707,8 @@ class TaskContextBuilder:
1683
1707
  resolved_since_ref=_delta_baseline.get("resolved_ref") if task_name == "delta" else None,
1684
1708
  resolution_path=_delta_baseline.get("resolution_path") if task_name == "delta" else None,
1685
1709
  diff_validation_status=_delta_baseline.get("diff_validation_status") if task_name == "delta" else None,
1710
+ warnings=_delta_baseline.get("warnings", []) if task_name == "delta" else [],
1711
+ symptom_hint=symptom_hint if task_name == "fix-bug" else None,
1686
1712
  )
1687
1713
 
1688
1714
  def render_prompt(self, output: TaskOutput) -> str:
@@ -3261,6 +3287,7 @@ class TaskContextBuilder:
3261
3287
  "resolution_path": "head_minus_1_fallback",
3262
3288
  "diff_validation_status": "invalid_ref", # original ref unresolved
3263
3289
  "error": False,
3290
+ "warnings": [{"code": "REF_NOT_FOUND", "ref": since, "resolved_to": "HEAD~1"}],
3264
3291
  }
3265
3292
 
3266
3293
  # All stages failed
sourcecode/serializer.py CHANGED
@@ -248,6 +248,16 @@ def _compact_git_context(sm: "SourceMap") -> "Optional[dict[str, Any]]":
248
248
  for f, n in _fc.most_common(5)
249
249
  ]
250
250
  ctx["hotspots_source"] = "recent_commits"
251
+ if gc.recent_commits:
252
+ ctx["recent_commits"] = [
253
+ {
254
+ "hash": c.hash[:8],
255
+ "message": (c.message or "")[:80],
256
+ "date": (c.date or "")[:10],
257
+ "author": c.author or "",
258
+ }
259
+ for c in gc.recent_commits[:5]
260
+ ]
251
261
  return ctx if ctx else None
252
262
 
253
263
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.30.27
3
+ Version: 1.30.28
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.27-blue)
224
+ ![Version](https://img.shields.io/badge/version-1.30.28-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.27
260
+ # sourcecode 1.30.28
261
261
  ```
262
262
 
263
263
  ---
@@ -1,10 +1,10 @@
1
- sourcecode/__init__.py,sha256=dwmvjucHTiWMZcDP2j41KFmIxCmu9Ib5cScfJsnEz_Q,104
1
+ sourcecode/__init__.py,sha256=q-bCo7uGXns3U7O05C-GiuHhFOyvaN-90_kC3JSQMn0,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=xNbK_xwsqdhUbAJT4VieJDitGlhMu_O_Y5xzY8yrc9Q,94497
7
+ sourcecode/cli.py,sha256=NbVyJAEQ4Q0ngkgW_lXyvlDJcD8TTrxZEXY6RIVkBAI,95261
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=4BwSEXUEuWiYaRZ8wPUcClSW5_Pl1BuUGe-VqM9MSPQ,166688
25
+ sourcecode/prepare_context.py,sha256=O3gpZIJLzCH2-9zr91zOP0D4gwHND2BbgBOCplwQXj8,168540
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=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
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=2Xi7bJUBiwsAGshy1a_CFgrYKudadakFAfKp_Lz2-qI,105749
36
+ sourcecode/serializer.py,sha256=svaLLYyAeAd3ZWsgh0YBwu7RKm00JaKuyz3bQbLipHA,106064
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.27.dist-info/METADATA,sha256=mi09pFkJXE7YELroOo3ewgHjwQp8je7jMFEvR2uiGYo,28956
68
- sourcecode-1.30.27.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
69
- sourcecode-1.30.27.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
70
- sourcecode-1.30.27.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
71
- sourcecode-1.30.27.dist-info/RECORD,,
67
+ sourcecode-1.30.28.dist-info/METADATA,sha256=cL4bkJZO4jc-T5k1GtC5OXS99NZLU4bZSl6WxVbo70w,28956
68
+ sourcecode-1.30.28.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
69
+ sourcecode-1.30.28.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
70
+ sourcecode-1.30.28.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
71
+ sourcecode-1.30.28.dist-info/RECORD,,