sourcecode 1.32.5__tar.gz → 1.32.7__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 (93) hide show
  1. {sourcecode-1.32.5 → sourcecode-1.32.7}/PKG-INFO +1 -1
  2. {sourcecode-1.32.5 → sourcecode-1.32.7}/pyproject.toml +1 -1
  3. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/__init__.py +1 -1
  4. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/cli.py +54 -2
  5. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/license.py +1 -0
  6. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/prepare_context.py +77 -0
  7. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/ris.py +13 -2
  8. {sourcecode-1.32.5 → sourcecode-1.32.7}/.github/workflows/build-windows.yml +0 -0
  9. {sourcecode-1.32.5 → sourcecode-1.32.7}/.gitignore +0 -0
  10. {sourcecode-1.32.5 → sourcecode-1.32.7}/.ruff.toml +0 -0
  11. {sourcecode-1.32.5 → sourcecode-1.32.7}/CHANGELOG.md +0 -0
  12. {sourcecode-1.32.5 → sourcecode-1.32.7}/CONTRIBUTING.md +0 -0
  13. {sourcecode-1.32.5 → sourcecode-1.32.7}/LICENSE +0 -0
  14. {sourcecode-1.32.5 → sourcecode-1.32.7}/README.md +0 -0
  15. {sourcecode-1.32.5 → sourcecode-1.32.7}/SECURITY.md +0 -0
  16. {sourcecode-1.32.5 → sourcecode-1.32.7}/raw +0 -0
  17. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/adaptive_scanner.py +0 -0
  18. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/architecture_analyzer.py +0 -0
  19. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/architecture_summary.py +0 -0
  20. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/ast_extractor.py +0 -0
  21. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/cache.py +0 -0
  22. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/cache.tmp_new +0 -0
  23. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/canonical_ir.py +0 -0
  24. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/classifier.py +0 -0
  25. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/code_notes_analyzer.py +0 -0
  26. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/confidence_analyzer.py +0 -0
  27. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/context_scorer.py +0 -0
  28. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/context_summarizer.py +0 -0
  29. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/contract_model.py +0 -0
  30. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/contract_pipeline.py +0 -0
  31. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/coverage_parser.py +0 -0
  32. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/dependency_analyzer.py +0 -0
  33. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/__init__.py +0 -0
  34. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/base.py +0 -0
  35. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/csproj_parser.py +0 -0
  36. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/dart.py +0 -0
  37. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/dotnet.py +0 -0
  38. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/elixir.py +0 -0
  39. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/go.py +0 -0
  40. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/heuristic.py +0 -0
  41. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/hybrid.py +0 -0
  42. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/java.py +0 -0
  43. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/jvm_ext.py +0 -0
  44. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/nodejs.py +0 -0
  45. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/parsers.py +0 -0
  46. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/php.py +0 -0
  47. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/project.py +0 -0
  48. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/python.py +0 -0
  49. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/ruby.py +0 -0
  50. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/rust.py +0 -0
  51. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/systems.py +0 -0
  52. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/terraform.py +0 -0
  53. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/detectors/tooling.py +0 -0
  54. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/doc_analyzer.py +0 -0
  55. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/entrypoint_classifier.py +0 -0
  56. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/env_analyzer.py +0 -0
  57. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/file_classifier.py +0 -0
  58. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/flow_analyzer.py +0 -0
  59. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/git_analyzer.py +0 -0
  60. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/graph_analyzer.py +0 -0
  61. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp/__init__.py +0 -0
  62. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  63. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  64. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  65. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  66. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  67. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp/runner.py +0 -0
  68. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp/server.py +0 -0
  69. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/mcp_nudge.py +0 -0
  70. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/metrics_analyzer.py +0 -0
  71. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/output_budget.py +0 -0
  72. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/path_filters.py +0 -0
  73. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/pr_comment_renderer.py +0 -0
  74. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/progress.py +0 -0
  75. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/ranking_engine.py +0 -0
  76. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/redactor.py +0 -0
  77. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/relevance_scorer.py +0 -0
  78. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/repo_classifier.py +0 -0
  79. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/repository_ir.py +0 -0
  80. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/runtime_classifier.py +0 -0
  81. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/scanner.py +0 -0
  82. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/schema.py +0 -0
  83. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/semantic_analyzer.py +0 -0
  84. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/serializer.py +0 -0
  85. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/summarizer.py +0 -0
  86. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/telemetry/__init__.py +0 -0
  87. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/telemetry/config.py +0 -0
  88. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/telemetry/consent.py +0 -0
  89. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/telemetry/events.py +0 -0
  90. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/telemetry/filters.py +0 -0
  91. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/telemetry/transport.py +0 -0
  92. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/tree_utils.py +0 -0
  93. {sourcecode-1.32.5 → sourcecode-1.32.7}/src/sourcecode/workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.32.5
3
+ Version: 1.32.7
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.32.5"
7
+ version = "1.32.7"
8
8
  description = "Deterministic codebase context 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.32.5"
3
+ __version__ = "1.32.7"
@@ -384,6 +384,18 @@ def _get_command_with_preprocessing(typer_instance: Any) -> Any:
384
384
  # Refresh help text at invocation time so it reflects current license state.
385
385
  cmd.help = _build_help_text()
386
386
 
387
+ # Add [Pro] badge to Pro-gated option help strings for free-tier users.
388
+ try:
389
+ from sourcecode.license import is_pro as _lp_is_pro
390
+ except Exception:
391
+ _lp_is_pro = False
392
+ if not _lp_is_pro:
393
+ _PRO_OPTS = {"full"}
394
+ for _param in cmd.params:
395
+ if getattr(_param, "name", None) in _PRO_OPTS and getattr(_param, "help", None):
396
+ if "[Pro]" not in _param.help:
397
+ _param.help = _param.help + " [dim][Pro][/dim]"
398
+
387
399
  _orig_cmd_main = cmd.main
388
400
 
389
401
  def _cmd_main(args: Optional[list[str]] = None, **kwargs: Any) -> Any:
@@ -1442,12 +1454,23 @@ def main(
1442
1454
  if dependency_analyzer is not None
1443
1455
  else None
1444
1456
  )
1457
+ # Free-tier node cap: limit graph size to 10 nodes unless Pro.
1458
+ _effective_max_nodes = max_nodes
1459
+ try:
1460
+ from sourcecode.license import is_pro as _lp_is_pro_gm
1461
+ except Exception:
1462
+ _lp_is_pro_gm = False
1463
+ _FREE_NODE_CAP = 10
1464
+ if not _lp_is_pro_gm and graph_analyzer is not None:
1465
+ if _effective_max_nodes is None or _effective_max_nodes > _FREE_NODE_CAP:
1466
+ _effective_max_nodes = _FREE_NODE_CAP
1467
+
1445
1468
  module_graph = (
1446
1469
  graph_analyzer.merge_graphs(
1447
1470
  module_graphs,
1448
1471
  detail=graph_detail_typed,
1449
1472
  edge_kinds=parsed_graph_edges,
1450
- max_nodes=max_nodes,
1473
+ max_nodes=_effective_max_nodes,
1451
1474
  entry_points=entry_points,
1452
1475
  )
1453
1476
  if graph_analyzer is not None
@@ -1567,6 +1590,15 @@ def main(
1567
1590
  semantic_summary=sem_sum,
1568
1591
  )
1569
1592
 
1593
+ # Free-tier node cap for --semantics: limit semantic_symbols to 10 unless Pro.
1594
+ if semantic_analyzer is not None and sm.semantic_symbols:
1595
+ try:
1596
+ from sourcecode.license import is_pro as _lp_is_pro_sem
1597
+ except Exception:
1598
+ _lp_is_pro_sem = False
1599
+ if not _lp_is_pro_sem and len(sm.semantic_symbols) > _FREE_NODE_CAP:
1600
+ sm = replace(sm, semantic_symbols=sm.semantic_symbols[:_FREE_NODE_CAP])
1601
+
1570
1602
  # Runtime architecture — classify workspace packages for structural summaries
1571
1603
  if workspace_analysis.workspaces:
1572
1604
  from sourcecode.runtime_classifier import RuntimeClassifier
@@ -3721,6 +3753,24 @@ def config_cmd() -> None:
3721
3753
  typer.echo(" sourcecode telemetry status")
3722
3754
 
3723
3755
 
3756
+ # ── cold-start (RIS bootstrap for external MCP and agents) ───────────────────
3757
+
3758
+ @app.command("cold-start")
3759
+ def cold_start_cmd(
3760
+ path: Path = typer.Argument(Path("."), help="Repository path (default: current directory)"),
3761
+ ) -> None:
3762
+ """Output Repository Intelligence Snapshot bootstrap context as JSON.
3763
+
3764
+ Returns instantly from persisted RIS — zero re-analysis cost.
3765
+ status: cold_start_ready | cold_start_stale | no_ris
3766
+ """
3767
+ import json as _json
3768
+ from sourcecode.ris import get_cold_start_context as _gcs
3769
+ target = Path(path).resolve()
3770
+ result = _gcs(target)
3771
+ typer.echo(_json.dumps(result, indent=2, ensure_ascii=False))
3772
+
3773
+
3724
3774
  # ── analyze (legacy alias) ────────────────────────────────────────────────────
3725
3775
 
3726
3776
  @app.command("analyze", hidden=True)
@@ -4175,11 +4225,13 @@ def cache_warm_cmd(
4175
4225
  agent: bool = typer.Option(False, "--agent", help="Also warm agent view."),
4176
4226
  ) -> None:
4177
4227
  """Pre-populate the cache by running a fresh analysis."""
4228
+ import shutil as _shutil
4178
4229
  import subprocess as _sub
4179
4230
  import sys as _sys
4180
4231
  target = Path(path).resolve()
4181
4232
  typer.echo(f"Warming cache for {target} …", err=True)
4182
- cmd = [_sys.executable, "-m", "sourcecode", str(target)]
4233
+ _sc_bin = _shutil.which("sourcecode") or _sys.argv[0]
4234
+ cmd = [_sc_bin, str(target)]
4183
4235
  if compact:
4184
4236
  cmd.append("--compact")
4185
4237
  if agent:
@@ -248,6 +248,7 @@ def require_feature(feature_name: str) -> None:
248
248
  f"'{display}' requires a Pro license. "
249
249
  "Run: sourcecode activate <license_key>"
250
250
  ),
251
+ "upgrade_hint": "sourcecode activate <license_key>",
251
252
  }
252
253
  sys.stdout.write(json.dumps(payload, ensure_ascii=False) + "\n")
253
254
  sys.stdout.flush()
@@ -734,6 +734,77 @@ class TaskContextBuilder:
734
734
  def __init__(self, root: Path) -> None:
735
735
  self.root = root
736
736
 
737
+ # ------------------------------------------------------------------
738
+ # RIS fast-path: serve onboard/explain from warm snapshot (<50ms)
739
+ # ------------------------------------------------------------------
740
+
741
+ def _try_ris_fast_path(self, task_name: str, spec: "TaskSpec") -> Optional[TaskOutput]:
742
+ """Return TaskOutput from a warm RIS without running the full scan.
743
+
744
+ Only activated for onboard/explain when git HEAD matches stored RIS.
745
+ Falls through (returns None) on any error or cache miss.
746
+ """
747
+ try:
748
+ import subprocess as _sp
749
+ from sourcecode.ris import load_ris as _lris
750
+ _ris = _lris(self.root)
751
+ if _ris is None or not _ris.compact_summary:
752
+ return None
753
+ _r = _sp.run(
754
+ ["git", "-C", str(self.root), "rev-parse", "--short", "HEAD"],
755
+ capture_output=True, text=True, timeout=2,
756
+ )
757
+ if _r.returncode != 0 or _r.stdout.strip() != _ris.git_head:
758
+ return None
759
+
760
+ compact = _ris.compact_summary
761
+ struct = _ris.structural_map
762
+
763
+ entry_points = struct.get("entrypoints") or compact.get("entry_points") or []
764
+ controllers = struct.get("controllers") or []
765
+ services = struct.get("services") or []
766
+
767
+ _seen: set = set()
768
+ relevant: list[RelevantFile] = []
769
+ for fp in entry_points[:10]:
770
+ if isinstance(fp, str) and fp not in _seen:
771
+ _seen.add(fp)
772
+ relevant.append(RelevantFile(path=fp, role="entrypoint", score=1.0, reason="entry_point", why="Primary entry point"))
773
+ for fp in controllers[:8]:
774
+ if isinstance(fp, str) and fp not in _seen:
775
+ _seen.add(fp)
776
+ relevant.append(RelevantFile(path=fp, role="source", score=0.9, reason="controller", why="Controller"))
777
+ for fp in services[:8]:
778
+ if isinstance(fp, str) and fp not in _seen:
779
+ _seen.add(fp)
780
+ relevant.append(RelevantFile(path=fp, role="source", score=0.8, reason="service", why="Service layer"))
781
+
782
+ # compact_summary uses "analysis_gaps" (list[dict]) not "gaps" (list[str])
783
+ _raw_gaps = compact.get("gaps") or compact.get("analysis_gaps") or []
784
+ _gap_strs: list[str] = [
785
+ g.get("reason", str(g)) if isinstance(g, dict) else str(g)
786
+ for g in _raw_gaps
787
+ if g
788
+ ]
789
+
790
+ return TaskOutput(
791
+ task=task_name,
792
+ goal=spec.goal,
793
+ project_summary=compact.get("project_summary") or compact.get("summary"),
794
+ architecture_summary=compact.get("architecture_summary"),
795
+ relevant_files=relevant,
796
+ suspected_areas=[],
797
+ improvement_opportunities=[],
798
+ test_gaps=[],
799
+ key_dependencies=compact.get("key_dependencies") or [],
800
+ code_notes_summary=None,
801
+ limitations=[],
802
+ confidence=compact.get("confidence") or compact.get("confidence_summary") or "high",
803
+ gaps=_gap_strs,
804
+ )
805
+ except Exception:
806
+ return None
807
+
737
808
  def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False, include_config: bool = False, all_gaps: bool = False) -> TaskOutput:
738
809
  if task_name not in TASKS:
739
810
  raise ValueError(
@@ -741,6 +812,12 @@ class TaskContextBuilder:
741
812
  )
742
813
  spec = TASKS[task_name]
743
814
 
815
+ # ── RIS fast-path (onboard / explain only) ────────────────────────────
816
+ if task_name in ("onboard", "explain") and not fast:
817
+ _warm = self._try_ris_fast_path(task_name, spec)
818
+ if _warm is not None:
819
+ return _warm
820
+
744
821
  # ── 0. review-pr: git-first scope resolution (before any filesystem scan) ─
745
822
  _pr_git_root: Optional[Path] = None
746
823
  _pr_scope_files: Optional[list[str]] = None
@@ -362,7 +362,8 @@ def get_cold_start_context(repo_root: Path) -> dict:
362
362
  current_head = _current_git_head(repo_root)
363
363
  stale = bool(current_head and ris.git_head and current_head != ris.git_head)
364
364
 
365
- return {
365
+ endpoints = ris.api_surface.get("endpoints", [])
366
+ result: dict = {
366
367
  "status": "cold_start_stale" if stale else "cold_start_ready",
367
368
  "repo_id": ris.repo_id,
368
369
  "git_head": ris.git_head,
@@ -370,8 +371,18 @@ def get_cold_start_context(repo_root: Path) -> dict:
370
371
  "last_updated_at": ris.last_updated_at,
371
372
  "summary": ris.compact_summary,
372
373
  "entrypoints": ris.structural_map.get("entrypoints", []),
373
- "endpoints": ris.api_surface.get("endpoints", []),
374
+ "endpoints": endpoints,
374
375
  "hotspots": ris.git_context_snapshot.get("hotspots", []),
375
376
  }
377
+ if not endpoints:
378
+ _is_java = (repo_root / "pom.xml").exists() or \
379
+ (repo_root / "build.gradle").exists() or \
380
+ (repo_root / "build.gradle.kts").exists()
381
+ if _is_java:
382
+ result["endpoints_hint"] = (
383
+ "Java repo detected but no endpoint index found. "
384
+ "Call get_endpoints (or: sourcecode endpoints <path>) to populate."
385
+ )
386
+ return result
376
387
  except Exception:
377
388
  return {"status": "no_ris"}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes