sourcecode 1.31.24__py3-none-any.whl → 1.31.26__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 +1 -1
- sourcecode/cli.py +30 -1
- sourcecode/mcp/server.py +219 -68
- {sourcecode-1.31.24.dist-info → sourcecode-1.31.26.dist-info}/METADATA +3 -3
- {sourcecode-1.31.24.dist-info → sourcecode-1.31.26.dist-info}/RECORD +8 -8
- {sourcecode-1.31.24.dist-info → sourcecode-1.31.26.dist-info}/WHEEL +0 -0
- {sourcecode-1.31.24.dist-info → sourcecode-1.31.26.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.31.24.dist-info → sourcecode-1.31.26.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -745,7 +745,7 @@ def main(
|
|
|
745
745
|
"is meant for --agent mode. Use --agent --full for expanded output.",
|
|
746
746
|
err=True,
|
|
747
747
|
)
|
|
748
|
-
raise typer.Exit(code=
|
|
748
|
+
raise typer.Exit(code=1)
|
|
749
749
|
|
|
750
750
|
# P0-2 FIX: --full without --compact or --agent has no effect in contract/raw mode.
|
|
751
751
|
# Warn so the user knows the flag is not doing anything.
|
|
@@ -2655,6 +2655,12 @@ def repo_ir_cmd(
|
|
|
2655
2655
|
help="Output format: json (default) or yaml.",
|
|
2656
2656
|
show_default=True,
|
|
2657
2657
|
),
|
|
2658
|
+
copy: bool = typer.Option(
|
|
2659
|
+
False,
|
|
2660
|
+
"--copy",
|
|
2661
|
+
"-c",
|
|
2662
|
+
help="Copy output to system clipboard after a successful run. No-op when --output is used or clipboard is unavailable.",
|
|
2663
|
+
),
|
|
2658
2664
|
) -> None:
|
|
2659
2665
|
"""Deterministic symbol-level IR for Java repositories.
|
|
2660
2666
|
|
|
@@ -2764,6 +2770,9 @@ def repo_ir_cmd(
|
|
|
2764
2770
|
# Fallback for wrapped stdout without buffer (e.g. some test harnesses)
|
|
2765
2771
|
_sys.stdout.write(output)
|
|
2766
2772
|
_sys.stdout.write("\n")
|
|
2773
|
+
if copy:
|
|
2774
|
+
if _copy_to_clipboard(output):
|
|
2775
|
+
typer.echo("✓ copied to clipboard", err=True)
|
|
2767
2776
|
|
|
2768
2777
|
|
|
2769
2778
|
# ── impact (blast-radius / change-impact analysis) ────────────────────────────
|
|
@@ -2799,6 +2808,12 @@ def impact_cmd(
|
|
|
2799
2808
|
"--include-tests",
|
|
2800
2809
|
help="Include test files in analysis (excluded by default).",
|
|
2801
2810
|
),
|
|
2811
|
+
copy: bool = typer.Option(
|
|
2812
|
+
False,
|
|
2813
|
+
"--copy",
|
|
2814
|
+
"-c",
|
|
2815
|
+
help="Copy output to system clipboard after a successful run. No-op when --output is used or clipboard is unavailable.",
|
|
2816
|
+
),
|
|
2802
2817
|
) -> None:
|
|
2803
2818
|
"""Blast-radius analysis: who calls this class and what breaks if it changes?
|
|
2804
2819
|
|
|
@@ -2869,6 +2884,9 @@ def impact_cmd(
|
|
|
2869
2884
|
_sys.stdout.buffer.flush()
|
|
2870
2885
|
except AttributeError:
|
|
2871
2886
|
_sys.stdout.write(output + "\n")
|
|
2887
|
+
if copy:
|
|
2888
|
+
if _copy_to_clipboard(output):
|
|
2889
|
+
typer.echo("✓ copied to clipboard", err=True)
|
|
2872
2890
|
|
|
2873
2891
|
# Non-zero exit when target not found
|
|
2874
2892
|
if result.get("resolution") == "not_found":
|
|
@@ -2899,6 +2917,12 @@ def endpoints_cmd(
|
|
|
2899
2917
|
help="Output format: json (default) or yaml.",
|
|
2900
2918
|
show_default=True,
|
|
2901
2919
|
),
|
|
2920
|
+
copy: bool = typer.Option(
|
|
2921
|
+
False,
|
|
2922
|
+
"--copy",
|
|
2923
|
+
"-c",
|
|
2924
|
+
help="Copy output to system clipboard after a successful run. No-op when --output is used or clipboard is unavailable.",
|
|
2925
|
+
),
|
|
2902
2926
|
) -> None:
|
|
2903
2927
|
"""Extract REST API endpoint surface from Java source files.
|
|
2904
2928
|
|
|
@@ -2934,6 +2958,9 @@ def endpoints_cmd(
|
|
|
2934
2958
|
_sys.stdout.buffer.write(output.encode("utf-8"))
|
|
2935
2959
|
_sys.stdout.buffer.write(b"\n")
|
|
2936
2960
|
_sys.stdout.buffer.flush()
|
|
2961
|
+
if copy:
|
|
2962
|
+
if _copy_to_clipboard(output):
|
|
2963
|
+
typer.echo("✓ copied to clipboard", err=True)
|
|
2937
2964
|
|
|
2938
2965
|
|
|
2939
2966
|
# ── Enterprise Workflow Commands ──────────────────────────────────────────────
|
|
@@ -3522,6 +3549,7 @@ def mcp_init(
|
|
|
3522
3549
|
raise typer.Exit(code=1)
|
|
3523
3550
|
|
|
3524
3551
|
typer.echo("MCP integration active.")
|
|
3552
|
+
typer.echo(" Note: repo_path debe usar forward slashes: C:/Users/... o /ruta/unix")
|
|
3525
3553
|
typer.echo("")
|
|
3526
3554
|
|
|
3527
3555
|
# Post-write: validate config and warn if client not running
|
|
@@ -3669,6 +3697,7 @@ def mcp_status() -> None:
|
|
|
3669
3697
|
typer.echo(sep)
|
|
3670
3698
|
typer.echo(" Note: 'configured' and 'running' are checked independently.")
|
|
3671
3699
|
typer.echo(" A running app still needs restart after first-time config.")
|
|
3700
|
+
typer.echo(" Path: repo_path debe usar forward slashes: C:/Users/... o /ruta/unix")
|
|
3672
3701
|
typer.echo(" Setup: sourcecode mcp init")
|
|
3673
3702
|
typer.echo(" Remove: sourcecode mcp remove")
|
|
3674
3703
|
|
sourcecode/mcp/server.py
CHANGED
|
@@ -10,8 +10,10 @@ data is the parsed JSON object from the CLI output, not a shell string.
|
|
|
10
10
|
"""
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import concurrent.futures
|
|
13
14
|
import json
|
|
14
15
|
import os
|
|
16
|
+
import re
|
|
15
17
|
from typing import Any
|
|
16
18
|
|
|
17
19
|
from mcp.server.fastmcp import FastMCP
|
|
@@ -63,6 +65,29 @@ def _execute(args: list[str]) -> dict | CallToolResult:
|
|
|
63
65
|
return _ok(result)
|
|
64
66
|
|
|
65
67
|
|
|
68
|
+
_DEFAULT_TESTS_TIMEOUT_MS = 15_000
|
|
69
|
+
|
|
70
|
+
# Regex for MINGW paths: /c/some/path → C:/some/path
|
|
71
|
+
_MINGW_PATH_RE = re.compile(r"^/([a-zA-Z])(/.*)?$")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _normalize_repo_path(path: str) -> str:
|
|
75
|
+
"""Normalize repo_path for cross-platform compatibility (P0-2).
|
|
76
|
+
|
|
77
|
+
Handles two Windows-specific formats:
|
|
78
|
+
- MINGW/Git-Bash: /c/Users/... → C:/Users/...
|
|
79
|
+
- Backslash: C:\\Users\\... → C:/Users/...
|
|
80
|
+
Forward-slash paths (C:/Users/... or /unix/path) pass through unchanged.
|
|
81
|
+
"""
|
|
82
|
+
m = _MINGW_PATH_RE.match(path)
|
|
83
|
+
if m:
|
|
84
|
+
drive = m.group(1).upper()
|
|
85
|
+
rest = m.group(2) or "/"
|
|
86
|
+
path = f"{drive}:{rest}"
|
|
87
|
+
path = path.replace("\\", "/")
|
|
88
|
+
return path
|
|
89
|
+
|
|
90
|
+
|
|
66
91
|
@mcp.tool()
|
|
67
92
|
def get_compact_context(repo_path: str = ".", git_context: bool = False) -> dict:
|
|
68
93
|
"""Compact human/LLM summary of a repository (~1000-3000 tokens). USE THIS FIRST.
|
|
@@ -76,14 +101,22 @@ def get_compact_context(repo_path: str = ".", git_context: bool = False) -> dict
|
|
|
76
101
|
repo_path: absolute path to the repository (default: current working directory).
|
|
77
102
|
git_context: include git log and branch context in the analysis.
|
|
78
103
|
"""
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
104
|
+
_raw = repo_path
|
|
105
|
+
try:
|
|
106
|
+
if not isinstance(repo_path, str):
|
|
107
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
108
|
+
if not isinstance(git_context, bool):
|
|
109
|
+
return _err("git_context must be boolean", "INVALID_ARGUMENT")
|
|
110
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
111
|
+
args = [repo_path, "--compact"]
|
|
112
|
+
if git_context:
|
|
113
|
+
args.append("--git-context")
|
|
114
|
+
return _execute(args)
|
|
115
|
+
except Exception as exc:
|
|
116
|
+
return _err(
|
|
117
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
118
|
+
"INTERNAL_ERROR",
|
|
119
|
+
)
|
|
87
120
|
|
|
88
121
|
|
|
89
122
|
@mcp.tool()
|
|
@@ -99,14 +132,22 @@ def get_agent_context(repo_path: str = ".", git_context: bool = False) -> dict:
|
|
|
99
132
|
repo_path: absolute path to the repository (default: current working directory).
|
|
100
133
|
git_context: include git log and branch context in the analysis.
|
|
101
134
|
"""
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
135
|
+
_raw = repo_path
|
|
136
|
+
try:
|
|
137
|
+
if not isinstance(repo_path, str):
|
|
138
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
139
|
+
if not isinstance(git_context, bool):
|
|
140
|
+
return _err("git_context must be boolean", "INVALID_ARGUMENT")
|
|
141
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
142
|
+
args = [repo_path, "--agent"]
|
|
143
|
+
if git_context:
|
|
144
|
+
args.append("--git-context")
|
|
145
|
+
return _execute(args)
|
|
146
|
+
except Exception as exc:
|
|
147
|
+
return _err(
|
|
148
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
149
|
+
"INTERNAL_ERROR",
|
|
150
|
+
)
|
|
110
151
|
|
|
111
152
|
|
|
112
153
|
@mcp.tool()
|
|
@@ -125,9 +166,17 @@ def get_endpoints(repo_path: str = ".") -> dict:
|
|
|
125
166
|
@Authenticated, @PreAuthorize, @Secured, @SecurityRequirement, @M3FiltroSeguridad.
|
|
126
167
|
repo_path: absolute path to the repository (default: current working directory).
|
|
127
168
|
"""
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
169
|
+
_raw = repo_path
|
|
170
|
+
try:
|
|
171
|
+
if not isinstance(repo_path, str):
|
|
172
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
173
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
174
|
+
return _execute(["endpoints", repo_path])
|
|
175
|
+
except Exception as exc:
|
|
176
|
+
return _err(
|
|
177
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
178
|
+
"INTERNAL_ERROR",
|
|
179
|
+
)
|
|
131
180
|
|
|
132
181
|
|
|
133
182
|
@mcp.tool()
|
|
@@ -138,12 +187,20 @@ def get_module_context(repo_path: str = ".", module: str = "") -> dict:
|
|
|
138
187
|
repo_path: absolute path to the repository root.
|
|
139
188
|
module: subdirectory name relative to repo_path (e.g. 'src/auth', 'api', 'core').
|
|
140
189
|
"""
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
190
|
+
_raw = repo_path
|
|
191
|
+
try:
|
|
192
|
+
if not isinstance(repo_path, str):
|
|
193
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
194
|
+
if not isinstance(module, str) or not module.strip():
|
|
195
|
+
return _err("module must be a non-empty string", "INVALID_ARGUMENT")
|
|
196
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
197
|
+
module_path = repo_path.rstrip("/") + "/" + module.strip("/")
|
|
198
|
+
return _execute([module_path, "--compact"])
|
|
199
|
+
except Exception as exc:
|
|
200
|
+
return _err(
|
|
201
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
202
|
+
"INTERNAL_ERROR",
|
|
203
|
+
)
|
|
147
204
|
|
|
148
205
|
|
|
149
206
|
@mcp.tool()
|
|
@@ -154,11 +211,19 @@ def get_delta(repo_path: str = ".", since: str = "HEAD~1") -> dict:
|
|
|
154
211
|
repo_path: absolute path to the repository (default: current working directory).
|
|
155
212
|
since: git ref to diff against (e.g. HEAD~3, main, origin/main).
|
|
156
213
|
"""
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
214
|
+
_raw = repo_path
|
|
215
|
+
try:
|
|
216
|
+
if not isinstance(repo_path, str):
|
|
217
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
218
|
+
if not isinstance(since, str) or not since.strip():
|
|
219
|
+
return _err("since must be a non-empty git ref", "INVALID_ARGUMENT")
|
|
220
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
221
|
+
return _execute(["prepare-context", "delta", repo_path, "--since", since])
|
|
222
|
+
except Exception as exc:
|
|
223
|
+
return _err(
|
|
224
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
225
|
+
"INTERNAL_ERROR",
|
|
226
|
+
)
|
|
162
227
|
|
|
163
228
|
|
|
164
229
|
@mcp.tool()
|
|
@@ -175,9 +240,17 @@ def get_ir_summary(repo_path: str = ".") -> dict:
|
|
|
175
240
|
|
|
176
241
|
repo_path: absolute path to the Java repository (default: current working directory).
|
|
177
242
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
243
|
+
_raw = repo_path
|
|
244
|
+
try:
|
|
245
|
+
if not isinstance(repo_path, str):
|
|
246
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
247
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
248
|
+
return _execute(["repo-ir", repo_path, "--summary-only"])
|
|
249
|
+
except Exception as exc:
|
|
250
|
+
return _err(
|
|
251
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
252
|
+
"INTERNAL_ERROR",
|
|
253
|
+
)
|
|
181
254
|
|
|
182
255
|
|
|
183
256
|
@mcp.tool()
|
|
@@ -190,12 +263,20 @@ def fix_bug_context(repo_path: str = ".", symptom: str = "") -> dict:
|
|
|
190
263
|
symptom: optional error message or class name to focus the file ranking
|
|
191
264
|
(e.g. "NullPointerException in EstructuraRrHhRestController").
|
|
192
265
|
"""
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
266
|
+
_raw = repo_path
|
|
267
|
+
try:
|
|
268
|
+
if not isinstance(repo_path, str):
|
|
269
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
270
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
271
|
+
args = ["prepare-context", "fix-bug", repo_path]
|
|
272
|
+
if symptom and isinstance(symptom, str) and symptom.strip():
|
|
273
|
+
args.extend(["--symptom", symptom.strip()])
|
|
274
|
+
return _execute(args)
|
|
275
|
+
except Exception as exc:
|
|
276
|
+
return _err(
|
|
277
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
278
|
+
"INTERNAL_ERROR",
|
|
279
|
+
)
|
|
199
280
|
|
|
200
281
|
|
|
201
282
|
@mcp.tool()
|
|
@@ -208,12 +289,20 @@ def review_pr_context(repo_path: str = ".", since: str = "") -> dict:
|
|
|
208
289
|
since: git ref to diff against (e.g. HEAD~3, main, origin/main).
|
|
209
290
|
If omitted, diffs against uncommitted changes or HEAD~1 fallback.
|
|
210
291
|
"""
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
292
|
+
_raw = repo_path
|
|
293
|
+
try:
|
|
294
|
+
if not isinstance(repo_path, str):
|
|
295
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
296
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
297
|
+
args = ["prepare-context", "review-pr", repo_path]
|
|
298
|
+
if since and isinstance(since, str) and since.strip():
|
|
299
|
+
args.extend(["--since", since.strip()])
|
|
300
|
+
return _execute(args)
|
|
301
|
+
except Exception as exc:
|
|
302
|
+
return _err(
|
|
303
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
304
|
+
"INTERNAL_ERROR",
|
|
305
|
+
)
|
|
217
306
|
|
|
218
307
|
|
|
219
308
|
@mcp.tool()
|
|
@@ -223,9 +312,17 @@ def onboard_context(repo_path: str = ".") -> dict:
|
|
|
223
312
|
Maps to: sourcecode prepare-context onboard <repo_path>
|
|
224
313
|
repo_path: absolute path to the repository (default: current working directory).
|
|
225
314
|
"""
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
315
|
+
_raw = repo_path
|
|
316
|
+
try:
|
|
317
|
+
if not isinstance(repo_path, str):
|
|
318
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
319
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
320
|
+
return _execute(["prepare-context", "onboard", repo_path])
|
|
321
|
+
except Exception as exc:
|
|
322
|
+
return _err(
|
|
323
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
324
|
+
"INTERNAL_ERROR",
|
|
325
|
+
)
|
|
229
326
|
|
|
230
327
|
|
|
231
328
|
@mcp.tool()
|
|
@@ -236,9 +333,17 @@ def explain_context(repo_path: str = ".") -> dict:
|
|
|
236
333
|
Returns: project summary, architecture, entry points, key dependencies.
|
|
237
334
|
repo_path: absolute path to the repository (default: current working directory).
|
|
238
335
|
"""
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
336
|
+
_raw = repo_path
|
|
337
|
+
try:
|
|
338
|
+
if not isinstance(repo_path, str):
|
|
339
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
340
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
341
|
+
return _execute(["prepare-context", "explain", repo_path])
|
|
342
|
+
except Exception as exc:
|
|
343
|
+
return _err(
|
|
344
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
345
|
+
"INTERNAL_ERROR",
|
|
346
|
+
)
|
|
242
347
|
|
|
243
348
|
|
|
244
349
|
@mcp.tool()
|
|
@@ -249,9 +354,17 @@ def refactor_context(repo_path: str = ".") -> dict:
|
|
|
249
354
|
Returns: structural issues, coupling hotspots, improvement opportunities.
|
|
250
355
|
repo_path: absolute path to the repository (default: current working directory).
|
|
251
356
|
"""
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
357
|
+
_raw = repo_path
|
|
358
|
+
try:
|
|
359
|
+
if not isinstance(repo_path, str):
|
|
360
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
361
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
362
|
+
return _execute(["prepare-context", "refactor", repo_path])
|
|
363
|
+
except Exception as exc:
|
|
364
|
+
return _err(
|
|
365
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
366
|
+
"INTERNAL_ERROR",
|
|
367
|
+
)
|
|
255
368
|
|
|
256
369
|
|
|
257
370
|
@mcp.tool()
|
|
@@ -260,15 +373,45 @@ def generate_tests_context(repo_path: str = ".", include_all: bool = False) -> d
|
|
|
260
373
|
|
|
261
374
|
Maps to: sourcecode prepare-context generate-tests <repo_path> [--all]
|
|
262
375
|
Returns: test_gaps list of untested files ranked by risk.
|
|
376
|
+
On large repos (>2000 classes) analysis is bounded by SOURCECODE_TESTS_TIMEOUT_MS
|
|
377
|
+
(default: 15000 ms). If timeout elapses, returns truncated=true with partial results.
|
|
263
378
|
repo_path: absolute path to the repository (default: current working directory).
|
|
264
379
|
include_all: return full test_gaps list without truncating to top 20.
|
|
265
380
|
"""
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
381
|
+
_raw = repo_path
|
|
382
|
+
try:
|
|
383
|
+
if not isinstance(repo_path, str):
|
|
384
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
385
|
+
if not isinstance(include_all, bool):
|
|
386
|
+
return _err("include_all must be boolean", "INVALID_ARGUMENT")
|
|
387
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
388
|
+
args = ["prepare-context", "generate-tests", repo_path]
|
|
389
|
+
if include_all:
|
|
390
|
+
args.append("--all")
|
|
391
|
+
|
|
392
|
+
# P0-3: timeout guard — large repos can stall the stdio transport indefinitely.
|
|
393
|
+
timeout_ms = int(os.environ.get("SOURCECODE_TESTS_TIMEOUT_MS", str(_DEFAULT_TESTS_TIMEOUT_MS)))
|
|
394
|
+
timeout_s = timeout_ms / 1000.0
|
|
395
|
+
|
|
396
|
+
executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
|
397
|
+
future = executor.submit(_execute, args)
|
|
398
|
+
done, _not_done = concurrent.futures.wait([future], timeout=timeout_s)
|
|
399
|
+
if _not_done:
|
|
400
|
+
executor.shutdown(wait=False)
|
|
401
|
+
return _ok({
|
|
402
|
+
"truncated": True,
|
|
403
|
+
"truncated_reason": f"timeout_{timeout_ms // 1000}s" if timeout_ms >= 1000 else f"timeout_{timeout_ms}ms",
|
|
404
|
+
"files_analyzed": 0,
|
|
405
|
+
"results": [],
|
|
406
|
+
})
|
|
407
|
+
executor.shutdown(wait=False)
|
|
408
|
+
return future.result()
|
|
409
|
+
|
|
410
|
+
except Exception as exc:
|
|
411
|
+
return _err(
|
|
412
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
413
|
+
"INTERNAL_ERROR",
|
|
414
|
+
)
|
|
272
415
|
|
|
273
416
|
|
|
274
417
|
@mcp.tool()
|
|
@@ -289,14 +432,22 @@ def get_impact_context(repo_path: str = ".", target: str = "", depth: int = 4) -
|
|
|
289
432
|
repo_path: absolute path to the Java repository (default: current working directory).
|
|
290
433
|
depth: BFS depth for indirect caller traversal (1–8, default: 4).
|
|
291
434
|
"""
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
435
|
+
_raw = repo_path
|
|
436
|
+
try:
|
|
437
|
+
if not isinstance(repo_path, str):
|
|
438
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
439
|
+
if not isinstance(target, str) or not target.strip():
|
|
440
|
+
return _err("target must be a non-empty class name or FQN", "INVALID_ARGUMENT")
|
|
441
|
+
if not isinstance(depth, int) or depth < 1 or depth > 8:
|
|
442
|
+
return _err("depth must be an integer between 1 and 8", "INVALID_ARGUMENT")
|
|
443
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
444
|
+
args = ["impact", target.strip(), repo_path, "--depth", str(depth)]
|
|
445
|
+
return _execute(args)
|
|
446
|
+
except Exception as exc:
|
|
447
|
+
return _err(
|
|
448
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
|
|
449
|
+
"INTERNAL_ERROR",
|
|
450
|
+
)
|
|
300
451
|
|
|
301
452
|
|
|
302
453
|
_TELEMETRY_ACTIONS = frozenset({"status", "enable", "disable"})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.31.
|
|
3
|
+
Version: 1.31.26
|
|
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
|
-

|
|
229
229
|

|
|
230
230
|
|
|
231
231
|
---
|
|
@@ -263,7 +263,7 @@ pipx install sourcecode
|
|
|
263
263
|
|
|
264
264
|
```bash
|
|
265
265
|
sourcecode version
|
|
266
|
-
# sourcecode 1.31.
|
|
266
|
+
# sourcecode 1.31.26
|
|
267
267
|
```
|
|
268
268
|
|
|
269
269
|
---
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=_0ipTmYmgGPKfHgZjMg0xBa-rDMdz89QQ6mQQ0rq9ng,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=NZu0XICv__hkQGKzW2LNQLRqb1L28K2p_WQCQKS5Zlk,23141
|
|
8
8
|
sourcecode/classifier.py,sha256=yWeq6agTjkFa3zuNa-gdVIHtjoBoPoVlJnX-b7tdVJs,7851
|
|
9
|
-
sourcecode/cli.py,sha256=
|
|
9
|
+
sourcecode/cli.py,sha256=illoqX3tEq9E_i2QEP4aUWPwFxb1IYdWwUbOobv4pyw,151737
|
|
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
|
|
@@ -64,7 +64,7 @@ sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG6
|
|
|
64
64
|
sourcecode/detectors/tooling.py,sha256=8CKbtxwQoABP-WyBRNmdAmHDOvAH57AR1cF4UKuWEdQ,2074
|
|
65
65
|
sourcecode/mcp/__init__.py,sha256=XU4HfRGbdid8wdUA0x_4f7uKZD1z3mv_XUY_WU_T9Mw,179
|
|
66
66
|
sourcecode/mcp/runner.py,sha256=7PnFjKYbgxFeDnqVeSntXHxZX7ZtK3-krDkEuVjI24M,1386
|
|
67
|
-
sourcecode/mcp/server.py,sha256=
|
|
67
|
+
sourcecode/mcp/server.py,sha256=8dktvqpjk2dYdnTsE0KHjZFkAR-3AQNcC3n-ITMmwKk,19864
|
|
68
68
|
sourcecode/mcp/onboarding/__init__.py,sha256=sj2PWqEBmMc4zBNkomg89WtL0M6S7A9yb7_wAuSWNP4,66
|
|
69
69
|
sourcecode/mcp/onboarding/applier.py,sha256=yfSMT0NKdZsjavtLkC8yQ7OtkfepOl5IXGByqg6bdEY,1894
|
|
70
70
|
sourcecode/mcp/onboarding/backup.py,sha256=ihqGOR8QTX8HASRSEDyfFyXr5bkXrygPHamv4p9KTmk,1452
|
|
@@ -76,8 +76,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
76
76
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
77
77
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
78
78
|
sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
|
|
79
|
-
sourcecode-1.31.
|
|
80
|
-
sourcecode-1.31.
|
|
81
|
-
sourcecode-1.31.
|
|
82
|
-
sourcecode-1.31.
|
|
83
|
-
sourcecode-1.31.
|
|
79
|
+
sourcecode-1.31.26.dist-info/METADATA,sha256=SyYqOW4T_PHgFlrJp2dwlPbiET16QlhdlduJB3LAtzU,31103
|
|
80
|
+
sourcecode-1.31.26.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
81
|
+
sourcecode-1.31.26.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
82
|
+
sourcecode-1.31.26.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
83
|
+
sourcecode-1.31.26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|