sourcecode 1.31.29__py3-none-any.whl → 1.31.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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.31.29"
3
+ __version__ = "1.31.30"
sourcecode/cli.py CHANGED
@@ -760,8 +760,11 @@ def main(
760
760
  err=True,
761
761
  )
762
762
 
763
- # P0-2 FIX: --changed-only silently implies --compact; inform the user.
764
- if changed_only and not compact and not agent:
763
+ # P1-2 FIX: --changed-only silently implies --compact; inform only on TTY.
764
+ # PowerShell 5.1 interprets any stderr write (even with exit 0) as NativeCommandError.
765
+ # Gate on isatty() so pipeline consumers never see informational noise on stderr.
766
+ import sys as _sys_tty
767
+ if changed_only and not compact and not agent and _sys_tty.stderr.isatty():
765
768
  typer.echo(
766
769
  "[info] --changed-only implies --compact (bounding output to changed files).",
767
770
  err=True,
@@ -2184,7 +2187,11 @@ def prepare_context_cmd(
2184
2187
 
2185
2188
  target = path.resolve()
2186
2189
  if not target.exists() or not target.is_dir():
2187
- typer.echo(f"Error: '{target}' is not a valid directory.", err=True)
2190
+ _emit_error_json(
2191
+ "invalid_path",
2192
+ f"'{target}' is not a valid directory.",
2193
+ path=str(target),
2194
+ )
2188
2195
  raise typer.Exit(code=1)
2189
2196
 
2190
2197
  if dry_run:
@@ -2316,6 +2323,13 @@ def prepare_context_cmd(
2316
2323
  out["improvement_opportunities"] = output.improvement_opportunities
2317
2324
  if _task_include("test_gaps") and output.test_gaps:
2318
2325
  out["test_gaps"] = output.test_gaps
2326
+ # P0-2: fast-mode truncation transparency — always emit when truncated, even if test_gaps is []
2327
+ # Use `is True` (strict) so MagicMock objects in tests don't trigger this branch.
2328
+ if getattr(output, "truncated", False) is True:
2329
+ out["truncated"] = True
2330
+ _tr = getattr(output, "truncated_reason", None)
2331
+ if isinstance(_tr, str) and _tr:
2332
+ out["truncated_reason"] = _tr
2319
2333
  if _task_include("code_notes_summary") and output.code_notes_summary:
2320
2334
  out["code_notes_summary"] = output.code_notes_summary
2321
2335
  if _task_include("changed_files") and output.changed_files:
@@ -2704,7 +2718,11 @@ def repo_ir_cmd(
2704
2718
 
2705
2719
  root = path.resolve()
2706
2720
  if not root.is_dir():
2707
- typer.echo(f"Error: {root} is not a directory", err=True)
2721
+ _emit_error_json(
2722
+ "invalid_path",
2723
+ f"'{root}' is not a valid directory.",
2724
+ path=str(root),
2725
+ )
2708
2726
  raise typer.Exit(1)
2709
2727
 
2710
2728
  if files:
@@ -2856,7 +2874,11 @@ def impact_cmd(
2856
2874
 
2857
2875
  root = path.resolve()
2858
2876
  if not root.is_dir():
2859
- typer.echo(f"Error: {root} is not a directory", err=True)
2877
+ _emit_error_json(
2878
+ "invalid_path",
2879
+ f"'{root}' is not a valid directory.",
2880
+ path=str(root),
2881
+ )
2860
2882
  raise typer.Exit(1)
2861
2883
 
2862
2884
  file_list = find_java_files(root)
@@ -2959,7 +2981,11 @@ def endpoints_cmd(
2959
2981
 
2960
2982
  target = path.resolve()
2961
2983
  if not target.exists() or not target.is_dir():
2962
- typer.echo(f"Error: '{target}' is not a valid directory.", err=True)
2984
+ _emit_error_json(
2985
+ "invalid_path",
2986
+ f"'{target}' is not a valid directory.",
2987
+ path=str(target),
2988
+ )
2963
2989
  raise typer.Exit(code=1)
2964
2990
 
2965
2991
  data = _extract_java_endpoints(target)
@@ -3075,7 +3101,10 @@ def review_pr_cmd(
3075
3101
  help="Copy output to clipboard after a successful run.",
3076
3102
  ),
3077
3103
  ) -> None:
3078
- """[Pro] PR review: blast radius, risk ranking, execution paths, security/txn impact.
3104
+ """[Pro*] PR review: blast radius, risk ranking, execution paths, security/txn impact.
3105
+
3106
+ Note: [Pro*] label is reserved for a future licensing gate. This command currently
3107
+ runs without authentication. Behavior may change in a future version.
3079
3108
 
3080
3109
  \b
3081
3110
  Answers: "What does this PR break and how risky is it?"
@@ -3135,7 +3164,10 @@ def fix_bug_cmd(
3135
3164
  help="Copy output to clipboard after a successful run.",
3136
3165
  ),
3137
3166
  ) -> None:
3138
- """[Pro] Bug triage: risk-ranked files, suspected areas, related annotations.
3167
+ """[Pro*] Bug triage: risk-ranked files, suspected areas, related annotations.
3168
+
3169
+ Note: [Pro*] label is reserved for a future licensing gate. This command currently
3170
+ runs without authentication. Behavior may change in a future version.
3139
3171
 
3140
3172
  \b
3141
3173
  Answers: "Where in this codebase should I look to fix this symptom?"
@@ -3187,7 +3219,10 @@ def modernize_cmd(
3187
3219
  help="Copy output to clipboard after a successful run.",
3188
3220
  ),
3189
3221
  ) -> None:
3190
- """[Pro] Modernization planning: coupling, dead zones, risky modules, refactor candidates.
3222
+ """[Pro*] Modernization planning: coupling, dead zones, risky modules, refactor candidates.
3223
+
3224
+ Note: [Pro*] label is reserved for a future licensing gate. This command currently
3225
+ runs without authentication. Behavior may change in a future version.
3191
3226
 
3192
3227
  \b
3193
3228
  Answers: "Where should I refactor first, and what's safest to touch?"
@@ -3218,7 +3253,11 @@ def modernize_cmd(
3218
3253
 
3219
3254
  root = path.resolve()
3220
3255
  if not root.is_dir():
3221
- typer.echo(f"Error: {root} is not a directory", err=True)
3256
+ _emit_error_json(
3257
+ "invalid_path",
3258
+ f"'{root}' is not a valid directory.",
3259
+ path=str(root),
3260
+ )
3222
3261
  raise typer.Exit(1)
3223
3262
 
3224
3263
  file_list = find_java_files(root)
@@ -3443,6 +3482,24 @@ def mcp_serve() -> None:
3443
3482
  from sourcecode.mcp.server import mcp as _mcp
3444
3483
 
3445
3484
  log = logging.getLogger(__name__)
3485
+
3486
+ # P0-1: Strip UTF-8 BOM from stdin.buffer before the MCP server reads it.
3487
+ # PowerShell 5.1 on Windows writes \xEF\xBB\xBF at the start of stdin,
3488
+ # which breaks JSON parsing at line 1 column 1.
3489
+ # peek(3) loads bytes into BufferedReader's internal buffer without consuming;
3490
+ # read(3) discards only if the prefix is the UTF-8 BOM sequence.
3491
+ # No-op on Linux/macOS/Git Bash where stdin never starts with a BOM.
3492
+ # Guard: CliRunner / test stubs replace sys.stdin with StringIO (no .buffer).
3493
+ try:
3494
+ _stdin_buf = getattr(_sys.stdin, "buffer", None)
3495
+ if _stdin_buf is not None and hasattr(_stdin_buf, "peek"):
3496
+ _bom_prefix = _stdin_buf.peek(3)[:3]
3497
+ if _bom_prefix == b"\xef\xbb\xbf":
3498
+ _stdin_buf.read(3)
3499
+ log.info("sourcecode-mcp stripped UTF-8 BOM from stdin (PowerShell 5.1 workaround)")
3500
+ except Exception:
3501
+ pass # Never abort server startup over BOM detection
3502
+
3446
3503
  log.info("sourcecode-mcp starting (stdio transport)")
3447
3504
  try:
3448
3505
  _mcp.run()
@@ -388,6 +388,9 @@ class TaskOutput:
388
388
  deployment_risks: list[str] = field(default_factory=list)
389
389
  deployment: Optional[dict] = None
390
390
  entry_points_structured: Optional[dict] = None
391
+ # P0-2: fast-mode truncation transparency
392
+ truncated: bool = False
393
+ truncated_reason: Optional[str] = None
391
394
 
392
395
 
393
396
  @dataclass
@@ -1991,6 +1994,21 @@ class TaskContextBuilder:
1991
1994
  untested.sort(key=lambda p: (len(p.split("/")), p))
1992
1995
  test_gaps = untested[:15]
1993
1996
 
1997
+ # P0-2: fast mode truncation transparency for generate-tests.
1998
+ # When --fast is active the test-gap discovery block is skipped entirely,
1999
+ # so test_gaps stays []. Without a signal the receiver interprets [] as
2000
+ # "no gaps", which is incorrect. Emit explicit truncation metadata and
2001
+ # downgrade confidence so the agent knows the analysis is incomplete.
2002
+ _fast_truncated = fast and task_name == "generate-tests"
2003
+ _fast_truncated_reason = "fast mode skips test gap discovery" if _fast_truncated else None
2004
+ if _fast_truncated:
2005
+ import sys as _sys_warn
2006
+ _sys_warn.stderr.write(
2007
+ "[warn] prepare-context generate-tests --fast: test gap discovery skipped. "
2008
+ "Output will contain truncated=true and confidence=low.\n"
2009
+ )
2010
+ _sys_warn.stderr.flush()
2011
+
1994
2012
  # ── 8. Confidence + gaps ──────────────────────────────────────────────
1995
2013
  from sourcecode.confidence_analyzer import ConfidenceAnalyzer
1996
2014
  from dataclasses import asdict as _asdict
@@ -2012,6 +2030,9 @@ class TaskContextBuilder:
2012
2030
 
2013
2031
  conf_summary, analysis_gaps = ConfidenceAnalyzer().analyze(sm_for_conf)
2014
2032
  confidence = conf_summary.overall
2033
+ # P0-2: fast-mode truncation overrides confidence to signal incomplete analysis
2034
+ if _fast_truncated:
2035
+ confidence = "low"
2015
2036
  _has_mybatis = any(
2016
2037
  f.name == "MyBatis"
2017
2038
  for s in stacks
@@ -2130,6 +2151,9 @@ class TaskContextBuilder:
2130
2151
  deployment_risks=_cb_deploy_risks,
2131
2152
  deployment=_cb_deployment,
2132
2153
  entry_points_structured=_cb_bootstrap,
2154
+ # P0-2: fast-mode truncation transparency
2155
+ truncated=_fast_truncated,
2156
+ truncated_reason=_fast_truncated_reason,
2133
2157
  )
2134
2158
 
2135
2159
  def render_prompt(self, output: TaskOutput) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.31.29
3
+ Version: 1.31.30
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -225,7 +225,7 @@ Description-Content-Type: text/markdown
225
225
 
226
226
  **AI-ready change intelligence for Java/Spring enterprise monoliths.**
227
227
 
228
- ![Version](https://img.shields.io/badge/version-1.31.29-blue)
228
+ ![Version](https://img.shields.io/badge/version-1.31.30-blue)
229
229
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
230
230
 
231
231
  ---
@@ -263,7 +263,7 @@ pipx install sourcecode
263
263
 
264
264
  ```bash
265
265
  sourcecode version
266
- # sourcecode 1.31.29
266
+ # sourcecode 1.31.30
267
267
  ```
268
268
 
269
269
  ---
@@ -1,4 +1,4 @@
1
- sourcecode/__init__.py,sha256=SBMk4PWIR6gOyohinC11lw1mhLWB_AxkxfACQb9B_zk,104
1
+ sourcecode/__init__.py,sha256=thS4KBhwEMTmgKe-XGLoD9avfVdy_-cdN7sf6C2_kA0,104
2
2
  sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
3
3
  sourcecode/architecture_analyzer.py,sha256=Ry3aYT9dc7XuLmWLT5IZ93RkCf_P14Qtew0nGPvUl_8,42184
4
4
  sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
@@ -6,7 +6,7 @@ sourcecode/ast_extractor.py,sha256=_btmeOJIe3t-NicF94D5ZAesa2YIJ0_QNExGnbHxGFE,5
6
6
  sourcecode/cache.py,sha256=TiYa3ECjBKtvlfCk7GvQ9v6gZkAITpH3ow9PubA7sUo,22946
7
7
  sourcecode/canonical_ir.py,sha256=_HM3AUmKSdna9u4dCoU6rpgSA6HdF8gzOKZykIUCNGY,23277
8
8
  sourcecode/classifier.py,sha256=yWeq6agTjkFa3zuNa-gdVIHtjoBoPoVlJnX-b7tdVJs,7851
9
- sourcecode/cli.py,sha256=KhYORJEuswP5T6Dn3bzIYATa01eHzUXmJjIMb5ZOBf4,152777
9
+ sourcecode/cli.py,sha256=7vwOxUdbWcKz42DnxKKp0TyxVhBmbl_eCOvcqct_gkw,155247
10
10
  sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
11
11
  sourcecode/confidence_analyzer.py,sha256=_jckZSxksV-OU38vbkxfVNBnWCtlCq8Vwfg23x1uspA,19054
12
12
  sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
@@ -27,7 +27,7 @@ sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7
27
27
  sourcecode/output_budget.py,sha256=43307mJEyUPU3MI-QEQoVxrcAvNyUzdzF_SAPgisBQE,6603
28
28
  sourcecode/path_filters.py,sha256=ROFRQ8eSLBEMiixK9f45-RO7um4VEEcjoD5AA4I427I,3739
29
29
  sourcecode/pr_comment_renderer.py,sha256=smHslxiG14lrytCkq5nFrFu-qTHgA-t-LFYfdrfjz2o,14423
30
- sourcecode/prepare_context.py,sha256=RxzyMoxgGmMdwqxMwd9CoEsOJARgKJ6YrZte_FeH68A,200168
30
+ sourcecode/prepare_context.py,sha256=RM7ka0rduJy8kwGHzLU9if6q7D9ST7tGjOf5LnsdTuw,201451
31
31
  sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
32
32
  sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,12970
33
33
  sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
@@ -77,8 +77,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
77
77
  sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
78
78
  sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
79
79
  sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
80
- sourcecode-1.31.29.dist-info/METADATA,sha256=x4Y5nAbzj_7MpUG9XvQFjbFIO0imXHfJWD1EjJ_Rkhc,31103
81
- sourcecode-1.31.29.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
82
- sourcecode-1.31.29.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
83
- sourcecode-1.31.29.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
84
- sourcecode-1.31.29.dist-info/RECORD,,
80
+ sourcecode-1.31.30.dist-info/METADATA,sha256=R8F2pFXTmotZdlErM06krEELPX74sev65gQk0WUvtrA,31103
81
+ sourcecode-1.31.30.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
82
+ sourcecode-1.31.30.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
83
+ sourcecode-1.31.30.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
84
+ sourcecode-1.31.30.dist-info/RECORD,,