sourcecode 1.33.20__tar.gz → 1.33.21__tar.gz

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.
Files changed (95) hide show
  1. {sourcecode-1.33.20 → sourcecode-1.33.21}/PKG-INFO +2 -2
  2. {sourcecode-1.33.20 → sourcecode-1.33.21}/README.md +1 -1
  3. {sourcecode-1.33.20 → sourcecode-1.33.21}/pyproject.toml +1 -1
  4. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/cli.py +70 -1
  6. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/server.py +35 -0
  7. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/repository_ir.py +8 -0
  8. {sourcecode-1.33.20 → sourcecode-1.33.21}/.github/workflows/build-windows.yml +0 -0
  9. {sourcecode-1.33.20 → sourcecode-1.33.21}/.gitignore +0 -0
  10. {sourcecode-1.33.20 → sourcecode-1.33.21}/.ruff.toml +0 -0
  11. {sourcecode-1.33.20 → sourcecode-1.33.21}/CHANGELOG.md +0 -0
  12. {sourcecode-1.33.20 → sourcecode-1.33.21}/CONTRIBUTING.md +0 -0
  13. {sourcecode-1.33.20 → sourcecode-1.33.21}/LICENSE +0 -0
  14. {sourcecode-1.33.20 → sourcecode-1.33.21}/SECURITY.md +0 -0
  15. {sourcecode-1.33.20 → sourcecode-1.33.21}/raw +0 -0
  16. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/adaptive_scanner.py +0 -0
  17. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/architecture_analyzer.py +0 -0
  18. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/architecture_summary.py +0 -0
  19. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/ast_extractor.py +0 -0
  20. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/cache.py +0 -0
  21. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/canonical_ir.py +0 -0
  22. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/classifier.py +0 -0
  23. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/code_notes_analyzer.py +0 -0
  24. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/confidence_analyzer.py +0 -0
  25. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/context_scorer.py +0 -0
  26. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/context_summarizer.py +0 -0
  27. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/contract_model.py +0 -0
  28. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/contract_pipeline.py +0 -0
  29. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/coverage_parser.py +0 -0
  30. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/dependency_analyzer.py +0 -0
  31. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/__init__.py +0 -0
  32. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/base.py +0 -0
  33. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/csproj_parser.py +0 -0
  34. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/dart.py +0 -0
  35. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/dotnet.py +0 -0
  36. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/elixir.py +0 -0
  37. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/go.py +0 -0
  38. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/heuristic.py +0 -0
  39. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/hybrid.py +0 -0
  40. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/java.py +0 -0
  41. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/jvm_ext.py +0 -0
  42. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/nodejs.py +0 -0
  43. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/parsers.py +0 -0
  44. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/php.py +0 -0
  45. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/project.py +0 -0
  46. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/python.py +0 -0
  47. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/ruby.py +0 -0
  48. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/rust.py +0 -0
  49. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/systems.py +0 -0
  50. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/terraform.py +0 -0
  51. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/detectors/tooling.py +0 -0
  52. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/doc_analyzer.py +0 -0
  53. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/entrypoint_classifier.py +0 -0
  54. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/env_analyzer.py +0 -0
  55. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/error_schema.py +0 -0
  56. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/file_classifier.py +0 -0
  57. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/flow_analyzer.py +0 -0
  58. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/git_analyzer.py +0 -0
  59. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/graph_analyzer.py +0 -0
  60. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/license.py +0 -0
  61. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/__init__.py +0 -0
  62. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  63. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  64. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  65. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  66. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  67. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/orchestrator.py +0 -0
  68. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/registry.py +0 -0
  69. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp/runner.py +0 -0
  70. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/mcp_nudge.py +0 -0
  71. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/metrics_analyzer.py +0 -0
  72. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/output_budget.py +0 -0
  73. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/path_filters.py +0 -0
  74. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/pr_comment_renderer.py +0 -0
  75. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/prepare_context.py +0 -0
  76. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/progress.py +0 -0
  77. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/ranking_engine.py +0 -0
  78. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/redactor.py +0 -0
  79. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/relevance_scorer.py +0 -0
  80. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/repo_classifier.py +0 -0
  81. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/ris.py +0 -0
  82. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/runtime_classifier.py +0 -0
  83. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/scanner.py +0 -0
  84. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/schema.py +0 -0
  85. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/semantic_analyzer.py +0 -0
  86. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/serializer.py +0 -0
  87. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/summarizer.py +0 -0
  88. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/telemetry/__init__.py +0 -0
  89. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/telemetry/config.py +0 -0
  90. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/telemetry/consent.py +0 -0
  91. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/telemetry/events.py +0 -0
  92. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/telemetry/filters.py +0 -0
  93. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/telemetry/transport.py +0 -0
  94. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/tree_utils.py +0 -0
  95. {sourcecode-1.33.20 → sourcecode-1.33.21}/src/sourcecode/workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.33.20
3
+ Version: 1.33.21
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -39,7 +39,7 @@ Description-Content-Type: text/markdown
39
39
 
40
40
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
41
41
 
42
- ![Version](https://img.shields.io/badge/version-1.33.20-blue)
42
+ ![Version](https://img.shields.io/badge/version-1.33.21-blue)
43
43
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
44
44
 
45
45
  ---
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
4
4
 
5
- ![Version](https://img.shields.io/badge/version-1.33.20-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.33.21-blue)
6
6
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
7
7
 
8
8
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.33.20"
7
+ version = "1.33.21"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.33.20"
3
+ __version__ = "1.33.21"
@@ -2566,6 +2566,42 @@ def prepare_context_cmd(
2566
2566
  from dataclasses import asdict
2567
2567
  import time as _time
2568
2568
 
2569
+ # Task-level cache: keyed on (task, git_head, symptom) so warm calls complete in <1s.
2570
+ # Skip for diff-dependent tasks (delta, review-pr), fast mode, and llm_prompt
2571
+ # (those embed per-call content that must not be served from cache).
2572
+ import subprocess as _pctx_sub
2573
+ import hashlib as _pctx_hash
2574
+ from sourcecode import cache as _pctx_cache
2575
+ _pctx_git_sha = ""
2576
+ _pctx_cache_key = ""
2577
+ _pctx_cacheable = task not in ("delta", "review-pr") and not fast and not llm_prompt
2578
+ if _pctx_cacheable:
2579
+ try:
2580
+ _sha_r2 = _pctx_sub.run(
2581
+ ["git", "-C", str(target), "rev-parse", "--short", "HEAD"],
2582
+ capture_output=True, text=True, timeout=3,
2583
+ )
2584
+ _pctx_git_sha = _sha_r2.stdout.strip()
2585
+ except Exception:
2586
+ pass
2587
+ if _pctx_git_sha:
2588
+ _sym_h = _pctx_hash.sha256((symptom or "").encode()).hexdigest()[:8]
2589
+ _pctx_cache_key = f"pctx-{task}-{_pctx_git_sha}-{_sym_h}-{format or 'json'}"
2590
+ _cached_pctx = _pctx_cache.read(target, _pctx_cache_key)
2591
+ if _cached_pctx is not None:
2592
+ if output_path is not None:
2593
+ output_path.write_text(_cached_pctx, encoding="utf-8")
2594
+ else:
2595
+ sys.stdout.buffer.write(_cached_pctx.encode("utf-8"))
2596
+ if not _cached_pctx.endswith("\n"):
2597
+ sys.stdout.buffer.write(b"\n")
2598
+ sys.stdout.buffer.flush()
2599
+ if copy:
2600
+ _c = _cached_pctx.strip()
2601
+ if _c not in ("{}", "[]", "null"):
2602
+ _copy_to_clipboard(_cached_pctx)
2603
+ return
2604
+
2569
2605
  builder = TaskContextBuilder(target)
2570
2606
  _progress = Progress()
2571
2607
  _phase = f"analyzing ({task})"
@@ -2950,6 +2986,12 @@ def prepare_context_cmd(
2950
2986
  else:
2951
2987
  _pc_content = json.dumps(out, indent=2, ensure_ascii=False)
2952
2988
 
2989
+ if _pctx_cacheable and _pctx_cache_key and format != "github-comment":
2990
+ try:
2991
+ _pctx_cache.write(target, _pctx_cache_key, _pc_content)
2992
+ except Exception:
2993
+ pass
2994
+
2953
2995
  if output_path is not None:
2954
2996
  output_path.write_text(_pc_content, encoding="utf-8")
2955
2997
  else:
@@ -3179,6 +3221,16 @@ def repo_ir_cmd(
3179
3221
  err=True,
3180
3222
  )
3181
3223
  else:
3224
+ _ir_size = len(output.encode("utf-8"))
3225
+ if _ir_size > 400_000:
3226
+ _ir_tokens = _ir_size // 4
3227
+ sys.stderr.write(
3228
+ f"WARNING: Output is ~{_ir_tokens // 1000}K tokens. This exceeds the context window of "
3229
+ "most LLMs (GPT-4o: 128K, Claude Sonnet: 200K). "
3230
+ "Use --compact or --agent for LLM-consumable context. "
3231
+ "Use --output FILE to save for local search tools.\n"
3232
+ )
3233
+ sys.stderr.flush()
3182
3234
  try:
3183
3235
  sys.stdout.buffer.write(output.encode("utf-8"))
3184
3236
  sys.stdout.buffer.write(b"\n")
@@ -3948,12 +4000,29 @@ def cold_start_cmd(
3948
4000
 
3949
4001
  Returns instantly from persisted RIS — zero re-analysis cost.
3950
4002
  status: cold_start_ready | cold_start_stale | no_ris
4003
+
4004
+ \b
4005
+ Note: Produces large output (~100K–200K tokens for medium repos).
4006
+ Designed for bootstrap snapshots, not direct LLM injection.
4007
+ Use --compact or --agent instead for LLM-consumable context.
4008
+ Use --output FILE to save for local search tools.
3951
4009
  """
3952
4010
  import json as _json
3953
4011
  from sourcecode.ris import get_cold_start_context as _gcs
3954
4012
  target = Path(path).resolve()
3955
4013
  result = _gcs(target)
3956
- typer.echo(_json.dumps(result, indent=2, ensure_ascii=False))
4014
+ _out = _json.dumps(result, indent=2, ensure_ascii=False)
4015
+ _size = len(_out.encode("utf-8"))
4016
+ if _size > 400_000:
4017
+ _tokens = _size // 4
4018
+ sys.stderr.write(
4019
+ f"WARNING: Output is ~{_tokens // 1000}K tokens. This exceeds the context window of "
4020
+ "most LLMs (GPT-4o: 128K, Claude Sonnet: 200K). "
4021
+ "Use --compact or --agent for LLM-consumable context. "
4022
+ "Use --output FILE to save for local search tools.\n"
4023
+ )
4024
+ sys.stderr.flush()
4025
+ typer.echo(_out)
3957
4026
 
3958
4027
 
3959
4028
  # ── analyze (legacy alias) ────────────────────────────────────────────────────
@@ -28,6 +28,41 @@ from sourcecode.error_schema import (
28
28
  )
29
29
  from sourcecode.mcp.runner import CommandError, run_command
30
30
 
31
+ # Patch FastMCP's Tool.run to intercept pydantic.ValidationError and return
32
+ # structured JSON instead of the raw "Error executing tool X: 1 validation
33
+ # error..." plain-text string that FastMCP produces by default.
34
+ try:
35
+ import pydantic as _pydantic
36
+ from mcp.server.fastmcp.tools.base import Tool as _FastMCPTool
37
+
38
+ _orig_tool_run = _FastMCPTool.run
39
+
40
+ async def _patched_tool_run(self, arguments, context=None, convert_result=False): # type: ignore[override]
41
+ try:
42
+ return await _orig_tool_run(self, arguments, context=context, convert_result=convert_result)
43
+ except Exception as _exc:
44
+ _cause = getattr(_exc, "__cause__", None)
45
+ if isinstance(_cause, _pydantic.ValidationError):
46
+ _errors = _cause.errors()
47
+ _missing = [str(e.get("loc", ("?",))[0]) for e in _errors if e.get("type") == "missing"]
48
+ _msg = f"Missing required field: {_missing[0]}" if _missing else "Argument validation failed"
49
+ _payload = {
50
+ "success": False,
51
+ "data": None,
52
+ "error": build_error_object(
53
+ INVALID_INPUT_CODE,
54
+ _msg,
55
+ hint="Pass the supported arguments using the documented tool schema.",
56
+ expected=f"{self.name} arguments with required field '{_missing[0]}'" if _missing else f"{self.name} arguments",
57
+ ),
58
+ }
59
+ return _payload
60
+ raise
61
+
62
+ _FastMCPTool.run = _patched_tool_run # type: ignore[method-assign]
63
+ except Exception:
64
+ pass # never break server startup over a patch failure
65
+
31
66
  # FIX-P0-5: MCP server version must match CLI version exactly.
32
67
  # FastMCP does not accept version= in __init__; inject it on the underlying
33
68
  # low-level Server so the MCP initialize handshake reports the correct version.
@@ -2399,6 +2399,8 @@ def _route_security_from_sym(
2399
2399
  m = _re2.search(r'(?:nombreRecurso\s*=\s*)?["\']([^"\']+)["\']', raw)
2400
2400
  if m:
2401
2401
  return {"policy": "custom_permission", "required_permission": m.group(1)}
2402
+ # Value is a constant reference or empty — still flag the annotation
2403
+ return {"policy": "custom_annotation", "annotation": "@M3FiltroSeguridad", "resource": raw.strip() or None}
2402
2404
  return None
2403
2405
 
2404
2406
  # Method-level first, then class-level fallback
@@ -2569,6 +2571,11 @@ def _build_route_surface(
2569
2571
  for child_fqn, parent_simple in extends_map.items()
2570
2572
  }
2571
2573
 
2574
+ # Build lookup for security_annotations from phase-2 routes
2575
+ _parent_sec_by_sym: dict[str, object] = {
2576
+ r["symbol"]: r.get("security_annotations") for r in routes
2577
+ }
2578
+
2572
2579
  for cls_simple, data in class_info.items():
2573
2580
  if data["own_endpoints"]:
2574
2581
  continue
@@ -2602,6 +2609,7 @@ def _build_route_surface(
2602
2609
  "method": verb,
2603
2610
  "stable_id": stable_id,
2604
2611
  "inheritance_depth": depth,
2612
+ "security_annotations": _parent_sec_by_sym.get(declaring_sym),
2605
2613
  })
2606
2614
  break
2607
2615
  chain = simple_extends.get(chain)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes