sourcecode 1.33.2__tar.gz → 1.33.3__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.
- {sourcecode-1.33.2 → sourcecode-1.33.3}/PKG-INFO +3 -3
- {sourcecode-1.33.2 → sourcecode-1.33.3}/README.md +2 -2
- {sourcecode-1.33.2 → sourcecode-1.33.3}/pyproject.toml +1 -1
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/cli.py +88 -2
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp/server.py +61 -6
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/ris.py +15 -9
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/serializer.py +7 -7
- {sourcecode-1.33.2 → sourcecode-1.33.3}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/.gitignore +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/.ruff.toml +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/CHANGELOG.md +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/CONTRIBUTING.md +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/LICENSE +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/SECURITY.md +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/raw +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/cache.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/cache.tmp_new +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/flow_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/license.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/repository_ir.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.33.2 → sourcecode-1.33.3}/src/sourcecode/workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.33.
|
|
3
|
+
Version: 1.33.3
|
|
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
|
-

|
|
43
43
|

|
|
44
44
|
|
|
45
45
|
---
|
|
@@ -113,7 +113,7 @@ pipx install sourcecode
|
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
115
|
sourcecode version
|
|
116
|
-
# sourcecode 1.33.
|
|
116
|
+
# sourcecode 1.33.3
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -76,7 +76,7 @@ pipx install sourcecode
|
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
78
|
sourcecode version
|
|
79
|
-
# sourcecode 1.33.
|
|
79
|
+
# sourcecode 1.33.3
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
---
|
|
@@ -4320,7 +4320,11 @@ def cache_warm_cmd(
|
|
|
4320
4320
|
compact: bool = typer.Option(True, "--compact/--no-compact", help="Warm compact view (default: on)."),
|
|
4321
4321
|
agent: bool = typer.Option(False, "--agent", help="Also warm agent view."),
|
|
4322
4322
|
) -> None:
|
|
4323
|
-
"""Pre-populate the cache by running a fresh analysis.
|
|
4323
|
+
"""Pre-populate the cache by running a fresh analysis.
|
|
4324
|
+
|
|
4325
|
+
Runs a full analysis to populate L1/L2 caches and rebuild the RIS
|
|
4326
|
+
(Repository Intelligence Snapshot). Useful after a merge/pull in CI.
|
|
4327
|
+
"""
|
|
4324
4328
|
import shutil as _shutil
|
|
4325
4329
|
import subprocess as _sub
|
|
4326
4330
|
import sys as _sys
|
|
@@ -4334,7 +4338,7 @@ def cache_warm_cmd(
|
|
|
4334
4338
|
cmd.append("--agent")
|
|
4335
4339
|
result = _sub.run(cmd, capture_output=True, text=True)
|
|
4336
4340
|
if result.returncode == 0:
|
|
4337
|
-
typer.echo("Cache warmed.", err=True)
|
|
4341
|
+
typer.echo("Cache warmed (L1/L2 + RIS rebuilt).", err=True)
|
|
4338
4342
|
else:
|
|
4339
4343
|
typer.echo(f"Warm failed (exit {result.returncode}).", err=True)
|
|
4340
4344
|
if result.stderr:
|
|
@@ -4342,6 +4346,88 @@ def cache_warm_cmd(
|
|
|
4342
4346
|
raise typer.Exit(code=result.returncode)
|
|
4343
4347
|
|
|
4344
4348
|
|
|
4349
|
+
@cache_app.command("freshness")
|
|
4350
|
+
def cache_freshness_cmd(
|
|
4351
|
+
path: Path = typer.Argument(Path("."), help="Repository path (default: current directory)"),
|
|
4352
|
+
json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
|
|
4353
|
+
) -> None:
|
|
4354
|
+
"""Report RIS freshness relative to the current git HEAD.
|
|
4355
|
+
|
|
4356
|
+
Answers: is the cached snapshot current? How many commits behind is it?
|
|
4357
|
+
|
|
4358
|
+
\b
|
|
4359
|
+
Output fields:
|
|
4360
|
+
fresh — True when RIS HEAD matches current HEAD and no uncommitted changes
|
|
4361
|
+
current_git_head — Current repo HEAD (short SHA)
|
|
4362
|
+
ris_git_head — HEAD stored in RIS when it was last built
|
|
4363
|
+
delta_commits — Number of commits between ris_git_head and HEAD (0 = in sync)
|
|
4364
|
+
has_uncommitted_changes — Working tree has staged/unstaged changes
|
|
4365
|
+
ris_exists — False when no RIS has been built yet
|
|
4366
|
+
ris_last_updated_at — ISO-8601 timestamp of last RIS write
|
|
4367
|
+
"""
|
|
4368
|
+
import json as _json
|
|
4369
|
+
import subprocess as _sub
|
|
4370
|
+
from sourcecode import cache as _cm
|
|
4371
|
+
from sourcecode.ris import _has_uncommitted_changes as _huc
|
|
4372
|
+
from sourcecode.ris import load_ris as _lris
|
|
4373
|
+
|
|
4374
|
+
target = Path(path).resolve()
|
|
4375
|
+
current_head = _cm._get_git_head(target)
|
|
4376
|
+
ris = _lris(target)
|
|
4377
|
+
|
|
4378
|
+
if ris is None:
|
|
4379
|
+
result: dict = {
|
|
4380
|
+
"fresh": False,
|
|
4381
|
+
"ris_exists": False,
|
|
4382
|
+
"current_git_head": current_head,
|
|
4383
|
+
"ris_git_head": None,
|
|
4384
|
+
"delta_commits": None,
|
|
4385
|
+
"has_uncommitted_changes": _huc(target),
|
|
4386
|
+
"ris_last_updated_at": None,
|
|
4387
|
+
}
|
|
4388
|
+
else:
|
|
4389
|
+
ris_head = ris.git_head
|
|
4390
|
+
head_matches = bool(current_head and ris_head and current_head == ris_head)
|
|
4391
|
+
uncommitted = _huc(target)
|
|
4392
|
+
|
|
4393
|
+
# Count commits between ris_head and current HEAD
|
|
4394
|
+
delta = None
|
|
4395
|
+
if ris_head and current_head and ris_head != current_head:
|
|
4396
|
+
try:
|
|
4397
|
+
_r = _sub.run(
|
|
4398
|
+
["git", "-C", str(target), "rev-list", "--count", f"{ris_head}..HEAD"],
|
|
4399
|
+
capture_output=True, text=True, timeout=5,
|
|
4400
|
+
)
|
|
4401
|
+
if _r.returncode == 0:
|
|
4402
|
+
delta = int(_r.stdout.strip())
|
|
4403
|
+
except Exception:
|
|
4404
|
+
pass
|
|
4405
|
+
elif head_matches:
|
|
4406
|
+
delta = 0
|
|
4407
|
+
|
|
4408
|
+
result = {
|
|
4409
|
+
"fresh": head_matches and not uncommitted,
|
|
4410
|
+
"ris_exists": True,
|
|
4411
|
+
"current_git_head": current_head,
|
|
4412
|
+
"ris_git_head": ris_head,
|
|
4413
|
+
"delta_commits": delta,
|
|
4414
|
+
"has_uncommitted_changes": uncommitted,
|
|
4415
|
+
"ris_last_updated_at": ris.last_updated_at,
|
|
4416
|
+
}
|
|
4417
|
+
|
|
4418
|
+
if json_output:
|
|
4419
|
+
typer.echo(_json.dumps(result, indent=2, ensure_ascii=False))
|
|
4420
|
+
else:
|
|
4421
|
+
_fresh_tag = "FRESH" if result["fresh"] else "STALE"
|
|
4422
|
+
typer.echo(f"Status: {_fresh_tag}")
|
|
4423
|
+
typer.echo(f"Current HEAD: {result['current_git_head'] or '(unknown)'}")
|
|
4424
|
+
typer.echo(f"RIS HEAD: {result.get('ris_git_head') or '(none)'}")
|
|
4425
|
+
if result.get("delta_commits") is not None:
|
|
4426
|
+
typer.echo(f"Delta: {result['delta_commits']} commit(s) behind")
|
|
4427
|
+
typer.echo(f"Uncommitted: {result['has_uncommitted_changes']}")
|
|
4428
|
+
typer.echo(f"RIS updated: {result.get('ris_last_updated_at') or 'never'}")
|
|
4429
|
+
|
|
4430
|
+
|
|
4345
4431
|
# ── Entry point ───────────────────────────────────────────────────────────────
|
|
4346
4432
|
|
|
4347
4433
|
def main_entry() -> None:
|
|
@@ -215,7 +215,10 @@ def get_agent_context(repo_path: str = ".", git_context: bool = False) -> dict:
|
|
|
215
215
|
|
|
216
216
|
@mcp.tool()
|
|
217
217
|
def get_endpoints(repo_path: str = ".") -> dict:
|
|
218
|
-
"""REST API endpoint surface extraction from Java source files.
|
|
218
|
+
"""REST API endpoint surface extraction from Java source files. JAVA ONLY.
|
|
219
|
+
|
|
220
|
+
Do NOT call this on non-Java repositories — it will return empty results.
|
|
221
|
+
Use get_compact_context or get_agent_context for non-Java repos.
|
|
219
222
|
|
|
220
223
|
Maps to: sourcecode endpoints <repo_path>
|
|
221
224
|
Returns: endpoints list with method, path, controller, handler fields;
|
|
@@ -230,7 +233,7 @@ def get_endpoints(repo_path: str = ".") -> dict:
|
|
|
230
233
|
Supports Spring MVC (@GetMapping etc.) and JAX-RS (@GET/@POST etc.).
|
|
231
234
|
Security annotations detected: @RolesAllowed, @PermitAll, @DenyAll,
|
|
232
235
|
@Authenticated, @PreAuthorize, @Secured, @SecurityRequirement, @M3FiltroSeguridad.
|
|
233
|
-
repo_path: absolute path to the repository (default: current working directory).
|
|
236
|
+
repo_path: absolute path to the Java repository (default: current working directory).
|
|
234
237
|
"""
|
|
235
238
|
_raw = repo_path
|
|
236
239
|
try:
|
|
@@ -275,25 +278,43 @@ def get_module_context(repo_path: str = ".", module: str = "") -> dict:
|
|
|
275
278
|
)
|
|
276
279
|
|
|
277
280
|
|
|
281
|
+
def _auto_since(repo_path: str) -> str:
|
|
282
|
+
"""Detect best merge-base for delta: origin/main > origin/master > HEAD~1."""
|
|
283
|
+
import subprocess as _sp
|
|
284
|
+
for base in ("origin/main", "origin/master"):
|
|
285
|
+
try:
|
|
286
|
+
r = _sp.run(
|
|
287
|
+
["git", "-C", repo_path, "merge-base", "HEAD", base],
|
|
288
|
+
capture_output=True, text=True, timeout=5,
|
|
289
|
+
)
|
|
290
|
+
if r.returncode == 0 and r.stdout.strip():
|
|
291
|
+
return r.stdout.strip()
|
|
292
|
+
except Exception:
|
|
293
|
+
pass
|
|
294
|
+
return "HEAD~1"
|
|
295
|
+
|
|
296
|
+
|
|
278
297
|
@mcp.tool()
|
|
279
|
-
def get_delta(repo_path: str = ".", since: str = "
|
|
298
|
+
def get_delta(repo_path: str = ".", since: str = "") -> dict:
|
|
280
299
|
"""Incremental context: git-changed files since a reference commit.
|
|
281
300
|
|
|
282
301
|
Maps to: sourcecode prepare-context delta <repo_path> --since <since>
|
|
283
302
|
repo_path: absolute path to the repository (default: current working directory).
|
|
284
303
|
since: git ref to diff against (e.g. HEAD~3, main, origin/main).
|
|
304
|
+
If empty or omitted, auto-detects merge-base with origin/main (or
|
|
305
|
+
origin/master). Falls back to HEAD~1 if no remote branch found.
|
|
306
|
+
Pass "HEAD~1" explicitly to force single-commit diff.
|
|
285
307
|
"""
|
|
286
308
|
_raw = repo_path
|
|
287
309
|
try:
|
|
288
310
|
if not isinstance(repo_path, str):
|
|
289
311
|
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
290
|
-
if not isinstance(since, str) or not since.strip():
|
|
291
|
-
return _err("since must be a non-empty git ref", "INVALID_ARGUMENT")
|
|
292
312
|
repo_path = _normalize_repo_path(repo_path)
|
|
293
313
|
_path_err = _check_repo_path(repo_path)
|
|
294
314
|
if _path_err is not None:
|
|
295
315
|
return _path_err
|
|
296
|
-
|
|
316
|
+
_since = since.strip() if isinstance(since, str) and since.strip() else _auto_since(repo_path)
|
|
317
|
+
return _execute(["prepare-context", "delta", repo_path, "--since", _since])
|
|
297
318
|
except Exception as exc:
|
|
298
319
|
return _err(
|
|
299
320
|
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
@@ -301,6 +322,40 @@ def get_delta(repo_path: str = ".", since: str = "HEAD~1") -> dict:
|
|
|
301
322
|
)
|
|
302
323
|
|
|
303
324
|
|
|
325
|
+
@mcp.tool()
|
|
326
|
+
def check_freshness(repo_path: str = ".") -> dict:
|
|
327
|
+
"""Report RIS freshness relative to the current git HEAD.
|
|
328
|
+
|
|
329
|
+
Answers instantly: is the cached snapshot current? How many commits behind?
|
|
330
|
+
Use before deciding whether to call get_compact_context for a refresh.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
fresh (bool) — True when RIS HEAD == current HEAD and no uncommitted changes
|
|
334
|
+
current_git_head (str) — Current repo HEAD (short SHA)
|
|
335
|
+
ris_git_head (str|null) — HEAD stored in RIS at last build
|
|
336
|
+
delta_commits (int|null) — Commits between ris_git_head and HEAD (0 = in sync)
|
|
337
|
+
has_uncommitted_changes — Working tree has staged or unstaged changes
|
|
338
|
+
ris_exists (bool) — False when no RIS built yet
|
|
339
|
+
ris_last_updated_at (str) — ISO-8601 timestamp of last RIS write
|
|
340
|
+
|
|
341
|
+
repo_path: absolute path to the repository (default: current working directory).
|
|
342
|
+
"""
|
|
343
|
+
_raw = repo_path
|
|
344
|
+
try:
|
|
345
|
+
if not isinstance(repo_path, str):
|
|
346
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
347
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
348
|
+
_path_err = _check_repo_path(repo_path)
|
|
349
|
+
if _path_err is not None:
|
|
350
|
+
return _path_err
|
|
351
|
+
return _execute(["cache", "freshness", repo_path, "--json"])
|
|
352
|
+
except Exception as exc:
|
|
353
|
+
return _err(
|
|
354
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path: {_raw}",
|
|
355
|
+
"INTERNAL_ERROR",
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
304
359
|
@mcp.tool()
|
|
305
360
|
def get_ir_summary(repo_path: str = ".") -> dict:
|
|
306
361
|
"""Deterministic symbol-level IR summary for Java repositories. Java only.
|
|
@@ -384,6 +384,15 @@ def get_cold_start_context(repo_root: Path) -> dict:
|
|
|
384
384
|
uncommitted = _has_uncommitted_changes(repo_root)
|
|
385
385
|
|
|
386
386
|
endpoints = ris.api_surface.get("endpoints", [])
|
|
387
|
+
_is_java = (
|
|
388
|
+
(repo_root / "pom.xml").exists()
|
|
389
|
+
or (repo_root / "build.gradle").exists()
|
|
390
|
+
or (repo_root / "build.gradle.kts").exists()
|
|
391
|
+
)
|
|
392
|
+
# api_surface_complete: False when this is a Java repo but endpoints are absent.
|
|
393
|
+
# An empty list does NOT mean "no endpoints exist" — it means the endpoint
|
|
394
|
+
# index has not been built yet. Agents must call get_endpoints to populate.
|
|
395
|
+
_api_complete = not _is_java or bool(endpoints)
|
|
387
396
|
result: dict = {
|
|
388
397
|
"status": "cold_start_stale" if stale else "cold_start_ready",
|
|
389
398
|
"repo_id": ris.repo_id,
|
|
@@ -394,20 +403,17 @@ def get_cold_start_context(repo_root: Path) -> dict:
|
|
|
394
403
|
"last_updated_at": ris.last_updated_at,
|
|
395
404
|
"cache_source": "RIS",
|
|
396
405
|
"data_scope": "RIS_BOOTSTRAP",
|
|
406
|
+
"api_surface_complete": _api_complete,
|
|
397
407
|
"summary": ris.compact_summary,
|
|
398
408
|
"entrypoints": ris.structural_map.get("entrypoints", []),
|
|
399
409
|
"endpoints": endpoints,
|
|
400
410
|
"hotspots": ris.git_context_snapshot.get("hotspots", []),
|
|
401
411
|
}
|
|
402
|
-
if not endpoints:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
result["endpoints_hint"] = (
|
|
408
|
-
"Java repo detected but no endpoint index found. "
|
|
409
|
-
"Call get_endpoints (or: sourcecode endpoints <path>) to populate."
|
|
410
|
-
)
|
|
412
|
+
if not endpoints and _is_java:
|
|
413
|
+
result["endpoints_hint"] = (
|
|
414
|
+
"Java repo detected but no endpoint index found. "
|
|
415
|
+
"Call get_endpoints (or: sourcecode endpoints <path>) to populate."
|
|
416
|
+
)
|
|
411
417
|
return result
|
|
412
418
|
except Exception:
|
|
413
419
|
return {"status": "no_ris"}
|
|
@@ -631,9 +631,12 @@ def _bootstrap_structured(eps: list) -> "Optional[dict[str, Any]]":
|
|
|
631
631
|
if security:
|
|
632
632
|
result["security"] = security
|
|
633
633
|
if controllers:
|
|
634
|
-
#
|
|
634
|
+
# Each controller file generates one EntryPoint regardless of how many
|
|
635
|
+
# handler methods it contains. controller_classes == len(controllers)
|
|
636
|
+
# always (deduplicated by path in _scan_java_file_for_entry_points).
|
|
637
|
+
# "methods" is therefore removed from the note — use `sourcecode endpoints`
|
|
638
|
+
# for per-method HTTP surface.
|
|
635
639
|
controller_classes = len({c["path"] for c in controllers})
|
|
636
|
-
controller_methods = len(controllers)
|
|
637
640
|
|
|
638
641
|
# Extract all DDD module names from controller paths and group by domain area.
|
|
639
642
|
# Path pattern: .../ddd/{module}/infrastructure/rest/*Controller.java
|
|
@@ -655,9 +658,8 @@ def _bootstrap_structured(eps: list) -> "Optional[dict[str, Any]]":
|
|
|
655
658
|
module_names.append(module)
|
|
656
659
|
|
|
657
660
|
_ctrl_note = (
|
|
658
|
-
f"{
|
|
659
|
-
f"
|
|
660
|
-
f" (use 'sourcecode endpoints' for full surface)"
|
|
661
|
+
f"{controller_classes} controller classes detected"
|
|
662
|
+
f" (use 'sourcecode endpoints' for per-method HTTP surface)"
|
|
661
663
|
)
|
|
662
664
|
if len(module_names) > 30:
|
|
663
665
|
# Group by first path segment under ddd/ (inferred domain area)
|
|
@@ -678,14 +680,12 @@ def _bootstrap_structured(eps: list) -> "Optional[dict[str, Any]]":
|
|
|
678
680
|
domain_groups[domain_prefix or "other"].append(module)
|
|
679
681
|
result["controllers"] = {
|
|
680
682
|
"classes": controller_classes,
|
|
681
|
-
"methods": controller_methods,
|
|
682
683
|
"note": _ctrl_note,
|
|
683
684
|
"modules": {k: sorted(v) for k, v in sorted(domain_groups.items())},
|
|
684
685
|
}
|
|
685
686
|
else:
|
|
686
687
|
result["controllers"] = {
|
|
687
688
|
"classes": controller_classes,
|
|
688
|
-
"methods": controller_methods,
|
|
689
689
|
"note": _ctrl_note,
|
|
690
690
|
"modules": sorted(module_names),
|
|
691
691
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|