sourcecode 1.30.30__py3-none-any.whl → 1.31.1__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 +328 -1
- sourcecode/mcp/__init__.py +5 -0
- sourcecode/mcp/onboarding/__init__.py +1 -0
- sourcecode/mcp/onboarding/applier.py +63 -0
- sourcecode/mcp/onboarding/backup.py +40 -0
- sourcecode/mcp/onboarding/detector.py +104 -0
- sourcecode/mcp/onboarding/planner.py +40 -0
- sourcecode/mcp/runner.py +47 -0
- sourcecode/mcp/server.py +119 -0
- {sourcecode-1.30.30.dist-info → sourcecode-1.31.1.dist-info}/METADATA +7 -3
- {sourcecode-1.30.30.dist-info → sourcecode-1.31.1.dist-info}/RECORD +15 -7
- {sourcecode-1.30.30.dist-info → sourcecode-1.31.1.dist-info}/WHEEL +0 -0
- {sourcecode-1.30.30.dist-info → sourcecode-1.31.1.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.30.30.dist-info → sourcecode-1.31.1.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -154,6 +154,10 @@ Compressed AI-ready context for Java/Spring enterprise codebases.
|
|
|
154
154
|
|
|
155
155
|
[bold]Subcommands:[/bold]
|
|
156
156
|
prepare-context TASK [PATH] [dim]# task-specific context (onboard, delta, fix-bug, ...)[/dim]
|
|
157
|
+
mcp init [dim]# setup MCP integration (Claude Desktop, Cursor)[/dim]
|
|
158
|
+
mcp status [dim]# show MCP integration status[/dim]
|
|
159
|
+
mcp remove [dim]# remove MCP integration safely[/dim]
|
|
160
|
+
mcp serve [dim]# start MCP server for AI agent integration[/dim]
|
|
157
161
|
telemetry status|enable|disable
|
|
158
162
|
version
|
|
159
163
|
"""
|
|
@@ -161,7 +165,7 @@ Compressed AI-ready context for Java/Spring enterprise codebases.
|
|
|
161
165
|
# Known subcommand names — tokens matching these are routed as subcommands,
|
|
162
166
|
# not consumed as a repository path.
|
|
163
167
|
_SUBCOMMANDS: frozenset[str] = frozenset(
|
|
164
|
-
{"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir"}
|
|
168
|
+
{"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir", "mcp"}
|
|
165
169
|
)
|
|
166
170
|
|
|
167
171
|
# Mutable container holding the path extracted by _preprocess_argv().
|
|
@@ -300,6 +304,9 @@ except Exception:
|
|
|
300
304
|
telemetry_app = typer.Typer(help="Manage anonymous telemetry (opt-in).", rich_markup_mode="rich")
|
|
301
305
|
app.add_typer(telemetry_app, name="telemetry")
|
|
302
306
|
|
|
307
|
+
mcp_app = typer.Typer(help="MCP integration: setup, status, serve, remove.", rich_markup_mode="rich")
|
|
308
|
+
app.add_typer(mcp_app, name="mcp")
|
|
309
|
+
|
|
303
310
|
|
|
304
311
|
def _maybe_ask_consent() -> None:
|
|
305
312
|
"""Show first-run consent prompt once, on interactive TTYs only."""
|
|
@@ -317,6 +324,26 @@ def _maybe_ask_consent() -> None:
|
|
|
317
324
|
pass
|
|
318
325
|
|
|
319
326
|
|
|
327
|
+
def _maybe_show_mcp_hint() -> None:
|
|
328
|
+
"""Show MCP integration hint once after first install, on TTY only."""
|
|
329
|
+
import sys as _sys
|
|
330
|
+
try:
|
|
331
|
+
if not _sys.stderr.isatty():
|
|
332
|
+
return
|
|
333
|
+
from sourcecode.telemetry.config import _CONFIG_FILE, _load, _save
|
|
334
|
+
data = _load()
|
|
335
|
+
if data.get("mcp", {}).get("hint_shown"):
|
|
336
|
+
return
|
|
337
|
+
typer.echo("", err=True)
|
|
338
|
+
typer.echo(" MCP integration available:", err=True)
|
|
339
|
+
typer.echo(" → sourcecode mcp init", err=True)
|
|
340
|
+
typer.echo("", err=True)
|
|
341
|
+
data.setdefault("mcp", {})["hint_shown"] = True
|
|
342
|
+
_save(data)
|
|
343
|
+
except Exception:
|
|
344
|
+
pass
|
|
345
|
+
|
|
346
|
+
|
|
320
347
|
def _active_flags(
|
|
321
348
|
dependencies: bool, graph_modules: bool, docs: bool, full_metrics: bool,
|
|
322
349
|
semantics: bool, architecture: bool, git_context: bool, env_map: bool,
|
|
@@ -616,6 +643,7 @@ def main(
|
|
|
616
643
|
# First-run consent (skip for telemetry/version/config subcommands)
|
|
617
644
|
if ctx.invoked_subcommand not in ("telemetry", "version", "config"):
|
|
618
645
|
_maybe_ask_consent()
|
|
646
|
+
_maybe_show_mcp_hint()
|
|
619
647
|
|
|
620
648
|
# When a subcommand is invoked, skip the main analysis.
|
|
621
649
|
if ctx.invoked_subcommand is not None:
|
|
@@ -2432,6 +2460,305 @@ def analyze_cmd(
|
|
|
2432
2460
|
raise typer.Exit(code=1)
|
|
2433
2461
|
|
|
2434
2462
|
|
|
2463
|
+
# ── MCP server ────────────────────────────────────────────────────────────────
|
|
2464
|
+
|
|
2465
|
+
@mcp_app.command("serve")
|
|
2466
|
+
def mcp_serve() -> None:
|
|
2467
|
+
"""Start the MCP server on stdio for AI agent integration.
|
|
2468
|
+
|
|
2469
|
+
\b
|
|
2470
|
+
Configure in your MCP client (e.g. Claude Desktop):
|
|
2471
|
+
{
|
|
2472
|
+
"sourcecode": {
|
|
2473
|
+
"command": "sourcecode",
|
|
2474
|
+
"args": ["mcp", "serve"]
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
"""
|
|
2478
|
+
import logging
|
|
2479
|
+
import sys as _sys
|
|
2480
|
+
|
|
2481
|
+
logging.basicConfig(
|
|
2482
|
+
stream=_sys.stderr,
|
|
2483
|
+
level=logging.INFO,
|
|
2484
|
+
format="[sourcecode-mcp] %(levelname)s %(message)s",
|
|
2485
|
+
)
|
|
2486
|
+
from sourcecode.mcp.server import mcp as _mcp
|
|
2487
|
+
|
|
2488
|
+
log = logging.getLogger(__name__)
|
|
2489
|
+
log.info("sourcecode-mcp starting (stdio transport)")
|
|
2490
|
+
try:
|
|
2491
|
+
_mcp.run()
|
|
2492
|
+
except KeyboardInterrupt:
|
|
2493
|
+
log.info("sourcecode-mcp stopped")
|
|
2494
|
+
except Exception as exc:
|
|
2495
|
+
log.critical("sourcecode-mcp fatal error: %s", exc, exc_info=True)
|
|
2496
|
+
raise typer.Exit(code=1)
|
|
2497
|
+
|
|
2498
|
+
|
|
2499
|
+
# ── MCP onboarding ────────────────────────────────────────────────────────────
|
|
2500
|
+
|
|
2501
|
+
@mcp_app.command("init")
|
|
2502
|
+
def mcp_init(
|
|
2503
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
|
|
2504
|
+
target: Optional[str] = typer.Option(
|
|
2505
|
+
None,
|
|
2506
|
+
"--target",
|
|
2507
|
+
"-t",
|
|
2508
|
+
help="Target client: claude-desktop | cursor. Default: auto-detect all.",
|
|
2509
|
+
),
|
|
2510
|
+
) -> None:
|
|
2511
|
+
"""Setup MCP integration for Claude Desktop, Cursor, and other clients.
|
|
2512
|
+
|
|
2513
|
+
\b
|
|
2514
|
+
Detects installed MCP clients, backs up their config files, and safely
|
|
2515
|
+
inserts the sourcecode server entry. Fully idempotent — safe to re-run.
|
|
2516
|
+
|
|
2517
|
+
\b
|
|
2518
|
+
Examples:
|
|
2519
|
+
sourcecode mcp init
|
|
2520
|
+
sourcecode mcp init --target claude-desktop
|
|
2521
|
+
sourcecode mcp init --target cursor --yes
|
|
2522
|
+
"""
|
|
2523
|
+
from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
|
|
2524
|
+
from sourcecode.mcp.onboarding.planner import build_install_plan
|
|
2525
|
+
from sourcecode.mcp.onboarding import backup, applier
|
|
2526
|
+
|
|
2527
|
+
typer.echo("Detecting MCP clients...")
|
|
2528
|
+
typer.echo("")
|
|
2529
|
+
|
|
2530
|
+
all_clients = detect_clients()
|
|
2531
|
+
|
|
2532
|
+
if target:
|
|
2533
|
+
target_slug = target.lower()
|
|
2534
|
+
clients = [c for c in all_clients if c.slug == target_slug]
|
|
2535
|
+
if not clients:
|
|
2536
|
+
valid = ", ".join(c.slug for c in all_clients)
|
|
2537
|
+
typer.echo(f"Unknown target '{target}'. Valid: {valid}", err=True)
|
|
2538
|
+
raise typer.Exit(code=1)
|
|
2539
|
+
else:
|
|
2540
|
+
clients = all_clients
|
|
2541
|
+
|
|
2542
|
+
if not clients:
|
|
2543
|
+
typer.echo("No MCP clients found on this system.")
|
|
2544
|
+
typer.echo("")
|
|
2545
|
+
typer.echo("Manual setup — add to your MCP client config:")
|
|
2546
|
+
typer.echo(' "sourcecode": {"command": "sourcecode", "args": ["mcp", "serve"]}')
|
|
2547
|
+
raise typer.Exit(code=0)
|
|
2548
|
+
|
|
2549
|
+
# Show detection results
|
|
2550
|
+
for client in clients:
|
|
2551
|
+
mark = "✓" if client.app_installed else "○"
|
|
2552
|
+
note = "" if client.app_installed else " (not found)"
|
|
2553
|
+
typer.echo(f" {mark} {client.name:<18} {client.config_path}{note}")
|
|
2554
|
+
typer.echo("")
|
|
2555
|
+
|
|
2556
|
+
# Build plan
|
|
2557
|
+
plan = build_install_plan(clients)
|
|
2558
|
+
actionable = [a for a in plan if a.client.app_installed and not a.already_installed]
|
|
2559
|
+
already_done = [a for a in plan if a.client.app_installed and a.already_installed]
|
|
2560
|
+
|
|
2561
|
+
if already_done and not actionable:
|
|
2562
|
+
typer.echo("Already configured:")
|
|
2563
|
+
for a in already_done:
|
|
2564
|
+
typer.echo(f" ✓ {a.client.name} {a.client.config_path}")
|
|
2565
|
+
typer.echo("")
|
|
2566
|
+
typer.echo("Nothing to do. Remove: sourcecode mcp remove")
|
|
2567
|
+
raise typer.Exit(code=0)
|
|
2568
|
+
|
|
2569
|
+
if already_done:
|
|
2570
|
+
typer.echo("Already configured:")
|
|
2571
|
+
for a in already_done:
|
|
2572
|
+
typer.echo(f" ✓ {a.client.name} {a.client.config_path}")
|
|
2573
|
+
typer.echo("")
|
|
2574
|
+
|
|
2575
|
+
# Show plan for actionable items
|
|
2576
|
+
typer.echo("This will:")
|
|
2577
|
+
for a in actionable:
|
|
2578
|
+
verb = "Create " if a.will_create_file else "Modify "
|
|
2579
|
+
typer.echo(f" {verb} {a.client.config_path}")
|
|
2580
|
+
typer.echo(f" Backup → ~/.config/sourcecode/mcp-backups/")
|
|
2581
|
+
typer.echo("")
|
|
2582
|
+
|
|
2583
|
+
if not yes:
|
|
2584
|
+
confirmed = typer.confirm("Proceed?", default=False)
|
|
2585
|
+
if not confirmed:
|
|
2586
|
+
typer.echo("Aborted.")
|
|
2587
|
+
raise typer.Exit(code=0)
|
|
2588
|
+
typer.echo("")
|
|
2589
|
+
|
|
2590
|
+
# Apply
|
|
2591
|
+
errors: list[str] = []
|
|
2592
|
+
for a in actionable:
|
|
2593
|
+
try:
|
|
2594
|
+
config = applier.read_config(a.client.config_path)
|
|
2595
|
+
if a.client.config_path.exists():
|
|
2596
|
+
bak = backup.create(a.client.config_path)
|
|
2597
|
+
typer.echo(f" ✓ Backup {bak}")
|
|
2598
|
+
updated = applier.apply_entry(config)
|
|
2599
|
+
applier.write_config(a.client.config_path, updated)
|
|
2600
|
+
if not applier.validate(a.client.config_path):
|
|
2601
|
+
errors.append(f"{a.client.name}: JSON validation failed after write")
|
|
2602
|
+
continue
|
|
2603
|
+
typer.echo(f" ✓ Updated {a.client.config_path}")
|
|
2604
|
+
except Exception as exc:
|
|
2605
|
+
errors.append(f"{a.client.name}: {exc}")
|
|
2606
|
+
|
|
2607
|
+
typer.echo("")
|
|
2608
|
+
|
|
2609
|
+
if errors:
|
|
2610
|
+
for err in errors:
|
|
2611
|
+
typer.echo(f" ✗ {err}", err=True)
|
|
2612
|
+
raise typer.Exit(code=1)
|
|
2613
|
+
|
|
2614
|
+
typer.echo("MCP integration active.")
|
|
2615
|
+
typer.echo("")
|
|
2616
|
+
|
|
2617
|
+
# Post-write: validate config and warn if client not running
|
|
2618
|
+
for a in actionable:
|
|
2619
|
+
if not is_client_running(a.client):
|
|
2620
|
+
typer.echo(
|
|
2621
|
+
f" ⚠ Config written but {a.client.name} is not running. "
|
|
2622
|
+
f"Start {a.client.name} and run sourcecode mcp status to verify.",
|
|
2623
|
+
err=False,
|
|
2624
|
+
)
|
|
2625
|
+
else:
|
|
2626
|
+
restart_msg = "" if a.will_create_file else f" Restart {a.client.name} to apply."
|
|
2627
|
+
typer.echo(f" ✓ {a.client.name} is running.{restart_msg}")
|
|
2628
|
+
|
|
2629
|
+
typer.echo("")
|
|
2630
|
+
typer.echo(" Remove: sourcecode mcp remove")
|
|
2631
|
+
|
|
2632
|
+
|
|
2633
|
+
@mcp_app.command("status")
|
|
2634
|
+
def mcp_status() -> None:
|
|
2635
|
+
"""Show MCP integration status: dependencies, config files, and connectivity."""
|
|
2636
|
+
from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
|
|
2637
|
+
from sourcecode.mcp.onboarding import applier
|
|
2638
|
+
|
|
2639
|
+
sep = "─" * 46
|
|
2640
|
+
|
|
2641
|
+
typer.echo("MCP Status")
|
|
2642
|
+
typer.echo(sep)
|
|
2643
|
+
|
|
2644
|
+
# Stage 1: Dependencies
|
|
2645
|
+
try:
|
|
2646
|
+
import mcp as _mcp_pkg # noqa: F401
|
|
2647
|
+
typer.echo("Dependencies ✓ installed")
|
|
2648
|
+
except ImportError:
|
|
2649
|
+
typer.echo("Dependencies ✗ missing")
|
|
2650
|
+
typer.echo(" Fix: pip install sourcecode[mcp]")
|
|
2651
|
+
typer.echo("")
|
|
2652
|
+
|
|
2653
|
+
clients = detect_clients()
|
|
2654
|
+
if not clients:
|
|
2655
|
+
typer.echo(" No MCP clients detected on this system.")
|
|
2656
|
+
typer.echo(sep)
|
|
2657
|
+
typer.echo(" Setup: sourcecode mcp init")
|
|
2658
|
+
raise typer.Exit(code=0)
|
|
2659
|
+
|
|
2660
|
+
# Stage 2: Config files
|
|
2661
|
+
typer.echo("Config files")
|
|
2662
|
+
for client in clients:
|
|
2663
|
+
if not client.app_installed:
|
|
2664
|
+
typer.echo(f" {client.name:<20} ✗ not found")
|
|
2665
|
+
typer.echo(f" Expected: {client.config_path}")
|
|
2666
|
+
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
2667
|
+
continue
|
|
2668
|
+
config = applier.read_config(client.config_path)
|
|
2669
|
+
if applier.is_installed(config):
|
|
2670
|
+
typer.echo(f" {client.name:<20} ✓ configured {client.config_path}")
|
|
2671
|
+
else:
|
|
2672
|
+
typer.echo(f" {client.name:<20} ✗ not configured")
|
|
2673
|
+
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
2674
|
+
typer.echo("")
|
|
2675
|
+
|
|
2676
|
+
# Stage 3: Connectivity
|
|
2677
|
+
typer.echo("Connectivity")
|
|
2678
|
+
any_installed = any(c.app_installed for c in clients)
|
|
2679
|
+
if not any_installed:
|
|
2680
|
+
typer.echo(" (no clients to check)")
|
|
2681
|
+
else:
|
|
2682
|
+
for client in clients:
|
|
2683
|
+
if not client.app_installed:
|
|
2684
|
+
continue
|
|
2685
|
+
if is_client_running(client):
|
|
2686
|
+
typer.echo(f" {client.name:<20} ✓ running")
|
|
2687
|
+
else:
|
|
2688
|
+
typer.echo(f" {client.name:<20} ✗ not running")
|
|
2689
|
+
typer.echo(f" Fix: open {client.name}, then run sourcecode mcp status")
|
|
2690
|
+
|
|
2691
|
+
typer.echo(sep)
|
|
2692
|
+
typer.echo(" Setup: sourcecode mcp init")
|
|
2693
|
+
typer.echo(" Remove: sourcecode mcp remove")
|
|
2694
|
+
|
|
2695
|
+
|
|
2696
|
+
@mcp_app.command("remove")
|
|
2697
|
+
def mcp_remove(
|
|
2698
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
|
|
2699
|
+
) -> None:
|
|
2700
|
+
"""Remove sourcecode MCP integration from all configured clients.
|
|
2701
|
+
|
|
2702
|
+
\b
|
|
2703
|
+
Backs up config files before modifying. Restores from backup when available,
|
|
2704
|
+
otherwise removes the sourcecode entry while preserving all other config.
|
|
2705
|
+
"""
|
|
2706
|
+
from sourcecode.mcp.onboarding.detector import detect_clients
|
|
2707
|
+
from sourcecode.mcp.onboarding.planner import build_remove_plan
|
|
2708
|
+
from sourcecode.mcp.onboarding import backup, applier
|
|
2709
|
+
|
|
2710
|
+
clients = detect_clients()
|
|
2711
|
+
plan = build_remove_plan(clients)
|
|
2712
|
+
installed = [a for a in plan if a.already_installed]
|
|
2713
|
+
|
|
2714
|
+
if not installed:
|
|
2715
|
+
typer.echo("sourcecode MCP integration not found in any client config.")
|
|
2716
|
+
typer.echo(" Setup: sourcecode mcp init")
|
|
2717
|
+
raise typer.Exit(code=0)
|
|
2718
|
+
|
|
2719
|
+
typer.echo("Remove sourcecode MCP integration from:")
|
|
2720
|
+
typer.echo("")
|
|
2721
|
+
for a in installed:
|
|
2722
|
+
typer.echo(f" {a.client.name} {a.client.config_path}")
|
|
2723
|
+
bak = backup.latest(a.client.config_path)
|
|
2724
|
+
if bak:
|
|
2725
|
+
typer.echo(f" Backup available: {bak}")
|
|
2726
|
+
typer.echo("")
|
|
2727
|
+
|
|
2728
|
+
if not yes:
|
|
2729
|
+
confirmed = typer.confirm("Proceed?", default=False)
|
|
2730
|
+
if not confirmed:
|
|
2731
|
+
typer.echo("Aborted.")
|
|
2732
|
+
raise typer.Exit(code=0)
|
|
2733
|
+
typer.echo("")
|
|
2734
|
+
|
|
2735
|
+
errors: list[str] = []
|
|
2736
|
+
for a in installed:
|
|
2737
|
+
try:
|
|
2738
|
+
bak = backup.create(a.client.config_path)
|
|
2739
|
+
typer.echo(f" ✓ Backup {bak}")
|
|
2740
|
+
config = applier.read_config(a.client.config_path)
|
|
2741
|
+
updated = applier.remove_entry(config)
|
|
2742
|
+
applier.write_config(a.client.config_path, updated)
|
|
2743
|
+
if not applier.validate(a.client.config_path):
|
|
2744
|
+
errors.append(f"{a.client.name}: JSON validation failed — restoring backup")
|
|
2745
|
+
backup.restore(bak, a.client.config_path)
|
|
2746
|
+
continue
|
|
2747
|
+
typer.echo(f" ✓ Updated {a.client.config_path}")
|
|
2748
|
+
except Exception as exc:
|
|
2749
|
+
errors.append(f"{a.client.name}: {exc}")
|
|
2750
|
+
|
|
2751
|
+
typer.echo("")
|
|
2752
|
+
|
|
2753
|
+
if errors:
|
|
2754
|
+
for err in errors:
|
|
2755
|
+
typer.echo(f" ✗ {err}", err=True)
|
|
2756
|
+
raise typer.Exit(code=1)
|
|
2757
|
+
|
|
2758
|
+
typer.echo("MCP integration removed.")
|
|
2759
|
+
typer.echo(" Re-add: sourcecode mcp init")
|
|
2760
|
+
|
|
2761
|
+
|
|
2435
2762
|
# ── Entry point ───────────────────────────────────────────────────────────────
|
|
2436
2763
|
|
|
2437
2764
|
def main_entry() -> None:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MCP client onboarding: detect, plan, apply, backup, remove."""
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Safe JSON config applier for MCP client configuration files."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
_MCP_SERVERS_KEY = "mcpServers"
|
|
8
|
+
_ENTRY_NAME = "sourcecode"
|
|
9
|
+
_ENTRY_VALUE: dict[str, object] = {
|
|
10
|
+
"command": "sourcecode",
|
|
11
|
+
"args": ["mcp", "serve"],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def read_config(path: Path) -> dict:
|
|
16
|
+
"""Parse JSON config from path. Returns empty dict if missing or empty."""
|
|
17
|
+
if not path.exists():
|
|
18
|
+
return {}
|
|
19
|
+
raw = path.read_text(encoding="utf-8").strip()
|
|
20
|
+
if not raw:
|
|
21
|
+
return {}
|
|
22
|
+
return json.loads(raw) # type: ignore[no-any-return]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_installed(config: dict) -> bool:
|
|
26
|
+
"""True if sourcecode entry already present in mcpServers."""
|
|
27
|
+
return _ENTRY_NAME in config.get(_MCP_SERVERS_KEY, {})
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def apply_entry(config: dict) -> dict:
|
|
31
|
+
"""Return new config dict with sourcecode merged into mcpServers."""
|
|
32
|
+
config = dict(config)
|
|
33
|
+
servers: dict = dict(config.get(_MCP_SERVERS_KEY, {}))
|
|
34
|
+
servers[_ENTRY_NAME] = _ENTRY_VALUE
|
|
35
|
+
config[_MCP_SERVERS_KEY] = servers
|
|
36
|
+
return config
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def remove_entry(config: dict) -> dict:
|
|
40
|
+
"""Return new config dict with sourcecode removed from mcpServers."""
|
|
41
|
+
config = dict(config)
|
|
42
|
+
servers: dict = dict(config.get(_MCP_SERVERS_KEY, {}))
|
|
43
|
+
servers.pop(_ENTRY_NAME, None)
|
|
44
|
+
if servers:
|
|
45
|
+
config[_MCP_SERVERS_KEY] = servers
|
|
46
|
+
elif _MCP_SERVERS_KEY in config:
|
|
47
|
+
del config[_MCP_SERVERS_KEY]
|
|
48
|
+
return config
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def write_config(path: Path, config: dict) -> None:
|
|
52
|
+
"""Atomically write config as formatted JSON."""
|
|
53
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def validate(path: Path) -> bool:
|
|
58
|
+
"""True if path contains parseable JSON."""
|
|
59
|
+
try:
|
|
60
|
+
json.loads(path.read_text(encoding="utf-8"))
|
|
61
|
+
return True
|
|
62
|
+
except Exception:
|
|
63
|
+
return False
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Timestamped backup management for MCP config files."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
_BACKUP_DIR = Path.home() / ".config" / "sourcecode" / "mcp-backups"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _backup_stem(config_path: Path) -> str:
|
|
11
|
+
"""Stable prefix derived from the config path, safe for filenames."""
|
|
12
|
+
parts = config_path.parts
|
|
13
|
+
# Use last two path components to keep names readable but unique enough.
|
|
14
|
+
label = "_".join(p for p in parts[-2:] if p).replace(".", "_")
|
|
15
|
+
return label
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def create(config_path: Path) -> Path:
|
|
19
|
+
"""Copy config_path to a timestamped backup file. Returns backup path."""
|
|
20
|
+
_BACKUP_DIR.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
ts = datetime.now().strftime("%Y%m%dT%H%M%S")
|
|
22
|
+
stem = _backup_stem(config_path)
|
|
23
|
+
backup_path = _BACKUP_DIR / f"{stem}.{ts}.bak"
|
|
24
|
+
backup_path.write_bytes(config_path.read_bytes())
|
|
25
|
+
return backup_path
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def restore(backup_path: Path, target_path: Path) -> None:
|
|
29
|
+
"""Overwrite target_path with contents of backup_path."""
|
|
30
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
target_path.write_bytes(backup_path.read_bytes())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def latest(config_path: Path) -> Path | None:
|
|
35
|
+
"""Find the most recent backup for config_path, or None."""
|
|
36
|
+
if not _BACKUP_DIR.exists():
|
|
37
|
+
return None
|
|
38
|
+
stem = _backup_stem(config_path)
|
|
39
|
+
matches = sorted(_BACKUP_DIR.glob(f"{stem}.*.bak"))
|
|
40
|
+
return matches[-1] if matches else None
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Detect MCP-capable clients installed on the current machine."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, List
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_CLIENT_REGISTRY: List[Dict[str, Any]] = [
|
|
13
|
+
{
|
|
14
|
+
"name": "Claude Desktop",
|
|
15
|
+
"slug": "claude-desktop",
|
|
16
|
+
"paths": {
|
|
17
|
+
"darwin": "~/Library/Application Support/Claude/claude_desktop_config.json",
|
|
18
|
+
"linux": "~/.config/Claude/claude_desktop_config.json",
|
|
19
|
+
"win32": "{APPDATA}/Claude/claude_desktop_config.json",
|
|
20
|
+
},
|
|
21
|
+
"process": {
|
|
22
|
+
"darwin": "Claude",
|
|
23
|
+
"linux": "claude-desktop",
|
|
24
|
+
"win32": "Claude",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "Cursor",
|
|
29
|
+
"slug": "cursor",
|
|
30
|
+
"paths": {
|
|
31
|
+
"darwin": "~/.cursor/mcp.json",
|
|
32
|
+
"linux": "~/.cursor/mcp.json",
|
|
33
|
+
"win32": "{USERPROFILE}/.cursor/mcp.json",
|
|
34
|
+
},
|
|
35
|
+
"process": {
|
|
36
|
+
"darwin": "Cursor",
|
|
37
|
+
"linux": "cursor",
|
|
38
|
+
"win32": "Cursor",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class MCPClient:
|
|
46
|
+
name: str
|
|
47
|
+
config_path: Path
|
|
48
|
+
app_installed: bool # True if the config file (or its parent dir) exists
|
|
49
|
+
process_name: str # OS process name for connectivity check
|
|
50
|
+
slug: str # --target identifier (e.g. "claude-desktop")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _resolve(template: str) -> Path:
|
|
54
|
+
"""Expand env vars in Windows-style {VAR} templates, then expanduser."""
|
|
55
|
+
result = template
|
|
56
|
+
for var in ("APPDATA", "LOCALAPPDATA", "USERPROFILE"):
|
|
57
|
+
val = os.environ.get(var, "")
|
|
58
|
+
if val:
|
|
59
|
+
result = result.replace(f"{{{var}}}", val)
|
|
60
|
+
return Path(result).expanduser()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def detect_clients() -> list[MCPClient]:
|
|
64
|
+
"""Return all known MCP clients with their resolved config paths."""
|
|
65
|
+
plat = sys.platform
|
|
66
|
+
clients: list[MCPClient] = []
|
|
67
|
+
for entry in _CLIENT_REGISTRY:
|
|
68
|
+
paths: Dict[str, str] = entry["paths"]
|
|
69
|
+
processes: Dict[str, str] = entry["process"]
|
|
70
|
+
template = paths.get(plat) or paths.get("linux", "")
|
|
71
|
+
if not template:
|
|
72
|
+
continue
|
|
73
|
+
config_path = _resolve(template)
|
|
74
|
+
app_installed = config_path.exists() or config_path.parent.exists()
|
|
75
|
+
process_name = processes.get(plat) or processes.get("linux", "")
|
|
76
|
+
clients.append(MCPClient(
|
|
77
|
+
name=entry["name"],
|
|
78
|
+
config_path=config_path,
|
|
79
|
+
app_installed=app_installed,
|
|
80
|
+
process_name=process_name,
|
|
81
|
+
slug=entry["slug"],
|
|
82
|
+
))
|
|
83
|
+
return clients
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def is_client_running(client: MCPClient) -> bool:
|
|
87
|
+
"""True if the client process is currently running."""
|
|
88
|
+
if not client.process_name:
|
|
89
|
+
return False
|
|
90
|
+
try:
|
|
91
|
+
if sys.platform == "win32":
|
|
92
|
+
result = subprocess.run(
|
|
93
|
+
["tasklist", "/fi", f"imagename eq {client.process_name}.exe"],
|
|
94
|
+
capture_output=True, text=True, timeout=5,
|
|
95
|
+
)
|
|
96
|
+
return client.process_name.lower() in result.stdout.lower()
|
|
97
|
+
else:
|
|
98
|
+
result = subprocess.run(
|
|
99
|
+
["pgrep", "-x", client.process_name],
|
|
100
|
+
capture_output=True, timeout=5,
|
|
101
|
+
)
|
|
102
|
+
return result.returncode == 0
|
|
103
|
+
except Exception:
|
|
104
|
+
return False
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Build an install/remove plan from detected MCP clients."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from .applier import is_installed, read_config
|
|
7
|
+
from .detector import MCPClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class ClientAction:
|
|
12
|
+
client: MCPClient
|
|
13
|
+
already_installed: bool # sourcecode entry already in config
|
|
14
|
+
will_create_file: bool # config file doesn't exist yet — will be created
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_install_plan(clients: list[MCPClient]) -> list[ClientAction]:
|
|
18
|
+
"""Describe what `mcp init` would do for each detected client."""
|
|
19
|
+
actions: list[ClientAction] = []
|
|
20
|
+
for client in clients:
|
|
21
|
+
config = read_config(client.config_path)
|
|
22
|
+
actions.append(ClientAction(
|
|
23
|
+
client=client,
|
|
24
|
+
already_installed=is_installed(config),
|
|
25
|
+
will_create_file=not client.config_path.exists(),
|
|
26
|
+
))
|
|
27
|
+
return actions
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def build_remove_plan(clients: list[MCPClient]) -> list[ClientAction]:
|
|
31
|
+
"""Describe what `mcp remove` would do for each detected client."""
|
|
32
|
+
actions: list[ClientAction] = []
|
|
33
|
+
for client in clients:
|
|
34
|
+
config = read_config(client.config_path)
|
|
35
|
+
actions.append(ClientAction(
|
|
36
|
+
client=client,
|
|
37
|
+
already_installed=is_installed(config),
|
|
38
|
+
will_create_file=False,
|
|
39
|
+
))
|
|
40
|
+
return actions
|
sourcecode/mcp/runner.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""In-process CLI runner for MCP tool execution.
|
|
2
|
+
|
|
3
|
+
Replaces the subprocess adapter from the standalone sourcecode-mcp project.
|
|
4
|
+
Calls CLI commands directly in the same process via CliRunner — no binary
|
|
5
|
+
lookup, no process fork, no stdout encoding issues.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from typer.testing import CliRunner
|
|
13
|
+
|
|
14
|
+
_runner = CliRunner()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def run_command(args: list[str]) -> Any:
|
|
18
|
+
"""Invoke a sourcecode CLI command in-process and return parsed output.
|
|
19
|
+
|
|
20
|
+
Returns parsed JSON dict when output is valid JSON, else the raw string.
|
|
21
|
+
Raises RuntimeError on non-zero exit or empty output.
|
|
22
|
+
"""
|
|
23
|
+
from sourcecode.cli import _detected_path, _preprocess_args, app
|
|
24
|
+
|
|
25
|
+
_detected_path[0] = "."
|
|
26
|
+
processed = _preprocess_args(list(args))
|
|
27
|
+
result = _runner.invoke(app, processed)
|
|
28
|
+
|
|
29
|
+
if result.exit_code != 0:
|
|
30
|
+
snippet = (result.output or "").strip()
|
|
31
|
+
raise RuntimeError(
|
|
32
|
+
f"sourcecode command failed (exit {result.exit_code}).\n"
|
|
33
|
+
f"Args: {args}\n"
|
|
34
|
+
f"Output: {snippet or '(empty)'}"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
output = (result.output or "").strip()
|
|
38
|
+
if not output:
|
|
39
|
+
raise RuntimeError(
|
|
40
|
+
f"sourcecode command produced no output.\n"
|
|
41
|
+
f"Args: {args}"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
return json.loads(output)
|
|
46
|
+
except json.JSONDecodeError:
|
|
47
|
+
return output
|
sourcecode/mcp/server.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""MCP server for sourcecode CLI.
|
|
2
|
+
|
|
3
|
+
Exposes sourcecode capabilities as MCP tools. Each tool maps to a CLI command
|
|
4
|
+
and delegates execution to the in-process runner — no subprocess, no binary
|
|
5
|
+
lookup, same process as the CLI.
|
|
6
|
+
|
|
7
|
+
All tools return:
|
|
8
|
+
{"success": bool, "data": dict | str | None, "error": {"code": str, "message": str} | None}
|
|
9
|
+
data is the parsed JSON object from the CLI output, not a shell string.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from mcp.server.fastmcp import FastMCP
|
|
17
|
+
|
|
18
|
+
from sourcecode.mcp.runner import run_command
|
|
19
|
+
|
|
20
|
+
mcp = FastMCP("sourcecode")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _ok(data: Any) -> dict:
|
|
24
|
+
return {"success": True, "data": data, "error": None}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _err(message: str, code: str = "EXECUTION_FAILED") -> dict:
|
|
28
|
+
return {"success": False, "data": None, "error": {"code": code, "message": message}}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _execute(args: list[str]) -> dict:
|
|
32
|
+
try:
|
|
33
|
+
return _ok(run_command(args))
|
|
34
|
+
except RuntimeError as exc:
|
|
35
|
+
return _err(str(exc))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@mcp.tool()
|
|
39
|
+
def get_compact_context(repo_path: str = ".") -> dict:
|
|
40
|
+
"""High-signal summary of a repository (~1000-3000 tokens).
|
|
41
|
+
|
|
42
|
+
Maps to: sourcecode <repo_path> --compact
|
|
43
|
+
Returns: stacks, entry points, dependency summary, confidence, gaps.
|
|
44
|
+
repo_path: absolute path to the repository (default: current working directory).
|
|
45
|
+
"""
|
|
46
|
+
if not isinstance(repo_path, str):
|
|
47
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
48
|
+
return _execute([repo_path, "--compact"])
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@mcp.tool()
|
|
52
|
+
def get_agent_context(repo_path: str = ".") -> dict:
|
|
53
|
+
"""Agent-optimised analysis: identity, entry points, dependencies, gaps.
|
|
54
|
+
|
|
55
|
+
Maps to: sourcecode <repo_path> --agent
|
|
56
|
+
Returns: structured noise-free JSON for AI agents.
|
|
57
|
+
repo_path: absolute path to the repository (default: current working directory).
|
|
58
|
+
"""
|
|
59
|
+
if not isinstance(repo_path, str):
|
|
60
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
61
|
+
return _execute([repo_path, "--agent"])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@mcp.tool()
|
|
65
|
+
def get_endpoints(repo_path: str = ".") -> dict:
|
|
66
|
+
"""API endpoint surface extraction.
|
|
67
|
+
|
|
68
|
+
Maps to: sourcecode <repo_path> --endpoints (pending CLI implementation)
|
|
69
|
+
repo_path: absolute path to the repository (default: current working directory).
|
|
70
|
+
"""
|
|
71
|
+
return _err(
|
|
72
|
+
"get_endpoints requires --endpoints CLI flag (pending implementation). "
|
|
73
|
+
"Use get_compact_context for now — the output includes api_endpoint-classified files.",
|
|
74
|
+
"NOT_IMPLEMENTED",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@mcp.tool()
|
|
79
|
+
def get_module_context(repo_path: str = ".", module: str = "") -> dict:
|
|
80
|
+
"""Compact analysis of a specific module or subdirectory within a repository.
|
|
81
|
+
|
|
82
|
+
Maps to: sourcecode <repo_path>/<module> --compact
|
|
83
|
+
repo_path: absolute path to the repository root.
|
|
84
|
+
module: subdirectory name relative to repo_path (e.g. 'src/auth', 'api', 'core').
|
|
85
|
+
"""
|
|
86
|
+
if not isinstance(repo_path, str):
|
|
87
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
88
|
+
if not isinstance(module, str) or not module.strip():
|
|
89
|
+
return _err("module must be a non-empty string", "INVALID_ARGUMENT")
|
|
90
|
+
module_path = os.path.join(repo_path, module)
|
|
91
|
+
return _execute([module_path, "--compact"])
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@mcp.tool()
|
|
95
|
+
def get_delta(repo_path: str = ".", since: str = "HEAD~1") -> dict:
|
|
96
|
+
"""Incremental context: git-changed files since a reference commit.
|
|
97
|
+
|
|
98
|
+
Maps to: sourcecode prepare-context delta <repo_path> --since <since>
|
|
99
|
+
repo_path: absolute path to the repository (default: current working directory).
|
|
100
|
+
since: git ref to diff against (e.g. HEAD~3, main, origin/main).
|
|
101
|
+
"""
|
|
102
|
+
if not isinstance(repo_path, str):
|
|
103
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
104
|
+
if not isinstance(since, str) or not since.strip():
|
|
105
|
+
return _err("since must be a non-empty git ref", "INVALID_ARGUMENT")
|
|
106
|
+
return _execute(["prepare-context", "delta", repo_path, "--since", since])
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@mcp.tool()
|
|
110
|
+
def get_ir_summary(repo_path: str = ".") -> dict:
|
|
111
|
+
"""Deterministic symbol-level IR summary for Java repositories.
|
|
112
|
+
|
|
113
|
+
Maps to: sourcecode repo-ir <repo_path> --summary-only
|
|
114
|
+
Returns: analysis summary, impact, and change_set — omits full graph nodes/edges.
|
|
115
|
+
repo_path: absolute path to the repository (default: current working directory).
|
|
116
|
+
"""
|
|
117
|
+
if not isinstance(repo_path, str):
|
|
118
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
119
|
+
return _execute(["repo-ir", repo_path, "--summary-only"])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.31.1
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -203,6 +203,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
203
203
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
204
204
|
Classifier: Topic :: Utilities
|
|
205
205
|
Requires-Python: >=3.9
|
|
206
|
+
Requires-Dist: mcp>=1.0.0
|
|
206
207
|
Requires-Dist: pathspec>=1.0
|
|
207
208
|
Requires-Dist: ruamel-yaml>=0.18
|
|
208
209
|
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
@@ -212,16 +213,19 @@ Requires-Dist: tree-sitter-javascript>=0.21; extra == 'ast'
|
|
|
212
213
|
Requires-Dist: tree-sitter-typescript>=0.21; extra == 'ast'
|
|
213
214
|
Requires-Dist: tree-sitter>=0.21; extra == 'ast'
|
|
214
215
|
Provides-Extra: dev
|
|
216
|
+
Requires-Dist: mcp>=1.0.0; extra == 'dev'
|
|
215
217
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
216
218
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
217
219
|
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
220
|
+
Provides-Extra: mcp
|
|
221
|
+
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
218
222
|
Description-Content-Type: text/markdown
|
|
219
223
|
|
|
220
224
|
# sourcecode
|
|
221
225
|
|
|
222
226
|
**Deterministic, behavior-aware codebase context for AI agents and PR review.**
|
|
223
227
|
|
|
224
|
-

|
|
225
229
|

|
|
226
230
|
|
|
227
231
|
---
|
|
@@ -257,7 +261,7 @@ pipx install sourcecode
|
|
|
257
261
|
|
|
258
262
|
```bash
|
|
259
263
|
sourcecode version
|
|
260
|
-
# sourcecode 1.
|
|
264
|
+
# sourcecode 1.31.1
|
|
261
265
|
```
|
|
262
266
|
|
|
263
267
|
---
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=6_eP1xYY6TSz7m1YdF9GvL5eOo0J9rRzaYP9OD56okA,103
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=MyBa0Hf5HmkudZQDLKrjcWDKETXETXl0mQX1swtTwAA,39091
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
|
|
5
5
|
sourcecode/ast_extractor.py,sha256=XgrZg2DcWcUm9r87cRG3KGO7IK2TIL_N-CvhSbUmmh4,49901
|
|
6
6
|
sourcecode/classifier.py,sha256=-0t0HLc9L9UleMLfclfLM3AXhBjUb_AYyBPDbvgWtac,7755
|
|
7
|
-
sourcecode/cli.py,sha256=
|
|
7
|
+
sourcecode/cli.py,sha256=I3h-Do1vFUEJzj7tK19yacZsYQA84t3Nlwfo1hNKjZo,112353
|
|
8
8
|
sourcecode/code_notes_analyzer.py,sha256=y1MJBnPZHYp4i6cQCXUb9ATIyifS_qMQWjw_8lPkpsU,9215
|
|
9
9
|
sourcecode/confidence_analyzer.py,sha256=H9VHYRzZhqMFlSCZffjtsMUGYLnDvrq1g5FjzyQ1hxE,16381
|
|
10
10
|
sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
|
|
@@ -58,14 +58,22 @@ sourcecode/detectors/rust.py,sha256=Tij1vz8BFZ332GEvVkL6vyMli2OMHJfHyDAppWfe66c,
|
|
|
58
58
|
sourcecode/detectors/systems.py,sha256=nYaKbGDFu0EOXFcd_1doWFT3tTUdkbxc2DjHUF5TcqQ,1627
|
|
59
59
|
sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG65hZ4,1693
|
|
60
60
|
sourcecode/detectors/tooling.py,sha256=8CKbtxwQoABP-WyBRNmdAmHDOvAH57AR1cF4UKuWEdQ,2074
|
|
61
|
+
sourcecode/mcp/__init__.py,sha256=XU4HfRGbdid8wdUA0x_4f7uKZD1z3mv_XUY_WU_T9Mw,179
|
|
62
|
+
sourcecode/mcp/runner.py,sha256=7PnFjKYbgxFeDnqVeSntXHxZX7ZtK3-krDkEuVjI24M,1386
|
|
63
|
+
sourcecode/mcp/server.py,sha256=Za6R1bGI7gQ9BI7M-UdBGK637W9wlowGcwjB0kRHZAs,4454
|
|
64
|
+
sourcecode/mcp/onboarding/__init__.py,sha256=sj2PWqEBmMc4zBNkomg89WtL0M6S7A9yb7_wAuSWNP4,66
|
|
65
|
+
sourcecode/mcp/onboarding/applier.py,sha256=yfSMT0NKdZsjavtLkC8yQ7OtkfepOl5IXGByqg6bdEY,1894
|
|
66
|
+
sourcecode/mcp/onboarding/backup.py,sha256=ihqGOR8QTX8HASRSEDyfFyXr5bkXrygPHamv4p9KTmk,1452
|
|
67
|
+
sourcecode/mcp/onboarding/detector.py,sha256=kDc0U6kXMuq_GivqwKrgJzIVLVeoLr3RQl63ksW10I8,3327
|
|
68
|
+
sourcecode/mcp/onboarding/planner.py,sha256=Fopg5f72FDiPfldF7NOxYjcBA_w8hi_jBJpSz39lPb8,1332
|
|
61
69
|
sourcecode/telemetry/__init__.py,sha256=M0eQZFNkmJiLbI_oNP4QEXwVju1dQ2d4P-E1-Bw8PxE,3116
|
|
62
70
|
sourcecode/telemetry/config.py,sha256=Pir0WHp4z-9Qclnn2NDZ3vwitqsMkOAJckmwjUSxrk4,1795
|
|
63
71
|
sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWnII,2237
|
|
64
72
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
65
73
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
66
74
|
sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
|
|
67
|
-
sourcecode-1.
|
|
68
|
-
sourcecode-1.
|
|
69
|
-
sourcecode-1.
|
|
70
|
-
sourcecode-1.
|
|
71
|
-
sourcecode-1.
|
|
75
|
+
sourcecode-1.31.1.dist-info/METADATA,sha256=rNR0kignzicNPRwVkhFULiLqGAbSqzLZgkGckgA0E-Y,29083
|
|
76
|
+
sourcecode-1.31.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
77
|
+
sourcecode-1.31.1.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
78
|
+
sourcecode-1.31.1.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
79
|
+
sourcecode-1.31.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|