sourcecode 2.0.0__py3-none-any.whl → 2.0.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 +2 -6
- sourcecode/cli.py +196 -199
- sourcecode/license.py +13 -31
- sourcecode/mcp/onboarding/applier.py +6 -14
- sourcecode/mcp/registry.py +22 -22
- sourcecode/mcp/runner.py +2 -2
- sourcecode/mcp/server.py +24 -24
- sourcecode/mcp_nudge.py +1 -1
- sourcecode/migrate_check.py +64 -0
- sourcecode/repository_ir.py +1 -1
- sourcecode/ris.py +1 -1
- sourcecode/serializer.py +1 -1
- sourcecode/summarizer.py +33 -2
- sourcecode/telemetry/consent.py +1 -1
- {sourcecode-2.0.0.dist-info → sourcecode-2.0.1.dist-info}/METADATA +114 -117
- {sourcecode-2.0.0.dist-info → sourcecode-2.0.1.dist-info}/RECORD +19 -19
- {sourcecode-2.0.0.dist-info → sourcecode-2.0.1.dist-info}/entry_points.txt +0 -1
- {sourcecode-2.0.0.dist-info → sourcecode-2.0.1.dist-info}/WHEEL +0 -0
- {sourcecode-2.0.0.dist-info → sourcecode-2.0.1.dist-info}/licenses/LICENSE +0 -0
sourcecode/cli.py
CHANGED
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import json
|
|
5
|
-
import os
|
|
6
5
|
import sys
|
|
7
6
|
import threading
|
|
8
7
|
import time
|
|
@@ -153,10 +152,10 @@ def _build_help_text() -> str:
|
|
|
153
152
|
if _is_pro:
|
|
154
153
|
plan_badge = "[bold green]● Pro[/bold green]"
|
|
155
154
|
else:
|
|
156
|
-
plan_badge = "[yellow]Free[/yellow] · [dim]
|
|
155
|
+
plan_badge = "[yellow]Free[/yellow] · [dim]sourcecode activate <key>[/dim] to unlock Pro"
|
|
157
156
|
|
|
158
157
|
text = f"""\
|
|
159
|
-
[bold]ASK Engine[/bold] [dim]· CLI:
|
|
158
|
+
[bold]ASK Engine[/bold] [dim]· CLI: sourcecode[/dim] {plan_badge}
|
|
160
159
|
|
|
161
160
|
Persistent structural context and ultra-fast repeated analysis for AI coding agents.
|
|
162
161
|
|
|
@@ -164,9 +163,9 @@ Cache warms on first scan; every subsequent call returns pre-built context in mi
|
|
|
164
163
|
Cold scan: 2–10s depending on repo size. Warm cache: 0.3–0.6s.
|
|
165
164
|
|
|
166
165
|
[bold]Primary usage:[/bold]
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
sourcecode --compact high-signal summary (~2,500–4,000 tokens)
|
|
167
|
+
sourcecode --compact --git-context include git hotspots and uncommitted files
|
|
168
|
+
sourcecode --agent full structured JSON for AI agents
|
|
170
169
|
|
|
171
170
|
[bold]Auth commands:[/bold]
|
|
172
171
|
auth status [dim]# show current plan and auth state[/dim]
|
|
@@ -178,11 +177,11 @@ Cold scan: 2–10s depending on repo size. Warm cache: 0.3–0.6s.
|
|
|
178
177
|
cache clear [dim]# clear all cached results for this repo[/dim]
|
|
179
178
|
|
|
180
179
|
[bold]Examples:[/bold]
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
sourcecode my-project --compact
|
|
181
|
+
sourcecode . --compact --git-context --copy
|
|
182
|
+
sourcecode . --changed-only --git-context
|
|
183
|
+
sourcecode prepare-context onboard my-project
|
|
184
|
+
sourcecode prepare-context delta . --since main
|
|
186
185
|
|
|
187
186
|
[bold]Subcommands:[/bold]
|
|
188
187
|
prepare-context TASK [PATH] [dim]# task-specific context (onboard, delta, fix-bug, ...)[/dim]
|
|
@@ -207,7 +206,7 @@ Cold scan: 2–10s depending on repo size. Warm cache: 0.3–0.6s.
|
|
|
207
206
|
|
|
208
207
|
[dim]Non-Java repos are free at any size. Local MCP serve is free.[/dim]
|
|
209
208
|
|
|
210
|
-
[dim cyan]→
|
|
209
|
+
[dim cyan]→ sourcecode activate <key>[/dim cyan]
|
|
211
210
|
"""
|
|
212
211
|
|
|
213
212
|
return text
|
|
@@ -524,7 +523,7 @@ def _copy_to_clipboard(content: str) -> bool:
|
|
|
524
523
|
|
|
525
524
|
|
|
526
525
|
app = typer.Typer(
|
|
527
|
-
name="
|
|
526
|
+
name="sourcecode",
|
|
528
527
|
help=_HELP,
|
|
529
528
|
add_completion=False,
|
|
530
529
|
rich_markup_mode="rich",
|
|
@@ -625,7 +624,7 @@ def _maybe_show_mcp_hint() -> None:
|
|
|
625
624
|
return
|
|
626
625
|
typer.echo("", err=True)
|
|
627
626
|
typer.echo(" MCP integration available:", err=True)
|
|
628
|
-
typer.echo(" →
|
|
627
|
+
typer.echo(" → sourcecode mcp init", err=True)
|
|
629
628
|
typer.echo("", err=True)
|
|
630
629
|
data.setdefault("mcp", {})["hint_shown"] = True
|
|
631
630
|
_save(data)
|
|
@@ -674,40 +673,47 @@ _IMPACT_PRIORITY_THRESHOLDS: list[tuple[float, str]] = [
|
|
|
674
673
|
|
|
675
674
|
def version_callback(value: bool) -> None:
|
|
676
675
|
if value:
|
|
677
|
-
typer.echo(f"
|
|
676
|
+
typer.echo(f"sourcecode {__version__}")
|
|
678
677
|
raise typer.Exit()
|
|
679
678
|
|
|
680
679
|
|
|
681
|
-
# ANSI Shadow block wordmark
|
|
682
|
-
# the
|
|
683
|
-
|
|
684
|
-
"
|
|
685
|
-
"
|
|
686
|
-
"
|
|
687
|
-
"
|
|
688
|
-
"
|
|
689
|
-
"╚═╝
|
|
680
|
+
# ANSI Shadow block wordmark, stacked "source" / "code" so it fits an 80-col
|
|
681
|
+
# terminal (the single-line "sourcecode" is 83 wide and would wrap).
|
|
682
|
+
_WELCOME_SOURCE = (
|
|
683
|
+
"███████╗ ██████╗ ██╗ ██╗██████╗ ██████╗███████╗",
|
|
684
|
+
"██╔════╝██╔═══██╗██║ ██║██╔══██╗██╔════╝██╔════╝",
|
|
685
|
+
"███████╗██║ ██║██║ ██║██████╔╝██║ █████╗ ",
|
|
686
|
+
"╚════██║██║ ██║██║ ██║██╔══██╗██║ ██╔══╝ ",
|
|
687
|
+
"███████║╚██████╔╝╚██████╔╝██║ ██║╚██████╗███████╗",
|
|
688
|
+
"╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝",
|
|
689
|
+
)
|
|
690
|
+
_WELCOME_CODE = (
|
|
691
|
+
" ██████╗ ██████╗ ██████╗ ███████╗",
|
|
692
|
+
"██╔════╝██╔═══██╗██╔══██╗██╔════╝",
|
|
693
|
+
"██║ ██║ ██║██║ ██║█████╗ ",
|
|
694
|
+
"██║ ██║ ██║██║ ██║██╔══╝ ",
|
|
695
|
+
"╚██████╗╚██████╔╝██████╔╝███████╗",
|
|
696
|
+
" ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝",
|
|
690
697
|
)
|
|
691
|
-
_WELCOME_ENGINE = "E N G I N E"
|
|
692
698
|
|
|
693
699
|
_WELCOME_CMDS = (
|
|
694
|
-
("
|
|
695
|
-
("
|
|
696
|
-
("
|
|
697
|
-
("
|
|
700
|
+
("sourcecode --compact", "repo summary"),
|
|
701
|
+
("sourcecode migrate-check", "upgrade readiness (free)"),
|
|
702
|
+
("sourcecode prepare-context onboard", "onboarding"),
|
|
703
|
+
("sourcecode mcp init", "connect IDE"),
|
|
698
704
|
)
|
|
699
705
|
|
|
700
706
|
|
|
701
707
|
def _print_welcome_plain(tier: str) -> None:
|
|
702
708
|
"""Plain-text welcome — fallback when rich is unavailable."""
|
|
703
|
-
lines = ["", f" ASK Engine {__version__} · {tier} · CLI:
|
|
709
|
+
lines = ["", f" ASK Engine {__version__} · {tier} · CLI: sourcecode", "",
|
|
704
710
|
" AI coding-agent context, instant.", "", " Get started:"]
|
|
705
711
|
for cmd, desc in _WELCOME_CMDS:
|
|
706
712
|
lines.append(f" {cmd.ljust(34)}{desc}")
|
|
707
713
|
lines.append("")
|
|
708
|
-
lines.append("
|
|
714
|
+
lines.append(" sourcecode --help all commands")
|
|
709
715
|
if tier != "Pro":
|
|
710
|
-
lines.append("
|
|
716
|
+
lines.append(" sourcecode activate <key> unlock Pro")
|
|
711
717
|
lines.append("")
|
|
712
718
|
typer.echo("\n".join(lines))
|
|
713
719
|
|
|
@@ -735,20 +741,20 @@ def _print_welcome() -> None:
|
|
|
735
741
|
|
|
736
742
|
pad = max(len(c) for c, _ in _WELCOME_CMDS) + 2
|
|
737
743
|
tier_style = "green" if tier != "Pro" else "magenta"
|
|
738
|
-
|
|
744
|
+
code_w = max(len(s) for s in _WELCOME_CODE)
|
|
739
745
|
|
|
740
746
|
t = Text()
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
747
|
+
for ln in _WELCOME_SOURCE:
|
|
748
|
+
t.append(ln + "\n", style="bold cyan")
|
|
749
|
+
# Append version + tier to the right of the "code" block's middle rows.
|
|
750
|
+
for i, ln in enumerate(_WELCOME_CODE):
|
|
751
|
+
t.append(ln.ljust(code_w), style="bold cyan")
|
|
744
752
|
if i == 2:
|
|
745
753
|
t.append(f" {__version__}", style="dim")
|
|
746
754
|
elif i == 3:
|
|
747
755
|
t.append(" ")
|
|
748
756
|
t.append(tier, style=tier_style)
|
|
749
757
|
t.append("\n")
|
|
750
|
-
# "ENGINE" label beneath the wordmark.
|
|
751
|
-
t.append(_WELCOME_ENGINE + "\n", style="bold dim cyan")
|
|
752
758
|
|
|
753
759
|
t.append("\nAI coding-agent context, instant.\n\n", style="white")
|
|
754
760
|
|
|
@@ -1010,10 +1016,10 @@ def main(
|
|
|
1010
1016
|
|
|
1011
1017
|
\b
|
|
1012
1018
|
Examples:
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1019
|
+
sourcecode --compact high-signal summary (recommended)
|
|
1020
|
+
sourcecode --compact --git-context include git hotspots
|
|
1021
|
+
sourcecode /path/to/repo --compact analyze specific path
|
|
1022
|
+
sourcecode --agent agent-optimized output (full detail)
|
|
1017
1023
|
"""
|
|
1018
1024
|
# First-run telemetry notice (skip for telemetry/version/config subcommands)
|
|
1019
1025
|
if ctx.invoked_subcommand not in ("telemetry", "version", "config"):
|
|
@@ -2771,14 +2777,14 @@ def prepare_context_cmd(
|
|
|
2771
2777
|
|
|
2772
2778
|
\b
|
|
2773
2779
|
Examples:
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2780
|
+
sourcecode prepare-context explain
|
|
2781
|
+
sourcecode prepare-context explain /path/to/repo
|
|
2782
|
+
sourcecode prepare-context fix-bug
|
|
2783
|
+
sourcecode prepare-context delta --since main
|
|
2784
|
+
sourcecode prepare-context review-pr --since origin/main
|
|
2785
|
+
sourcecode prepare-context review-pr . --since main --output review.json
|
|
2786
|
+
sourcecode prepare-context onboard --llm-prompt
|
|
2787
|
+
sourcecode prepare-context --task-help
|
|
2782
2788
|
"""
|
|
2783
2789
|
from sourcecode.prepare_context import TASKS, TaskContextBuilder
|
|
2784
2790
|
|
|
@@ -2829,7 +2835,7 @@ def prepare_context_cmd(
|
|
|
2829
2835
|
"free_tier_note": (
|
|
2830
2836
|
f"Free quota of {30} delta runs per repository exhausted."
|
|
2831
2837
|
),
|
|
2832
|
-
"free_tier_alternative": "
|
|
2838
|
+
"free_tier_alternative": "sourcecode prepare-context review-pr --since <ref>",
|
|
2833
2839
|
},
|
|
2834
2840
|
)
|
|
2835
2841
|
# Within quota: emit a header note so CI logs show remaining runs.
|
|
@@ -3341,7 +3347,7 @@ def telemetry_status() -> None:
|
|
|
3341
3347
|
if not asked:
|
|
3342
3348
|
typer.echo(" (first-run notice not yet shown — will show on next run)")
|
|
3343
3349
|
typer.echo(f" Config: {config_file_path()}")
|
|
3344
|
-
typer.echo(" Disable:
|
|
3350
|
+
typer.echo(" Disable: sourcecode telemetry disable")
|
|
3345
3351
|
typer.echo(" Or set env var: SOURCECODE_TELEMETRY=0 (or DO_NOT_TRACK=1)")
|
|
3346
3352
|
|
|
3347
3353
|
|
|
@@ -3354,7 +3360,7 @@ def telemetry_enable() -> None:
|
|
|
3354
3360
|
typer.echo("Telemetry enabled. Thank you — this helps improve sourcecode.")
|
|
3355
3361
|
typer.echo("What is collected: version, OS, commands, flags, duration, repo size range, errors.")
|
|
3356
3362
|
typer.echo("What is never collected: source code, paths, secrets, or any output content.")
|
|
3357
|
-
typer.echo("Disable at any time:
|
|
3363
|
+
typer.echo("Disable at any time: sourcecode telemetry disable")
|
|
3358
3364
|
_tel.record("telemetry_enabled", cmd="telemetry")
|
|
3359
3365
|
|
|
3360
3366
|
|
|
@@ -3365,7 +3371,7 @@ def telemetry_disable() -> None:
|
|
|
3365
3371
|
set_enabled(False)
|
|
3366
3372
|
typer.echo("Telemetry disabled. No data will be collected or sent.")
|
|
3367
3373
|
typer.echo("Telemetry is on by default; this opt-out is remembered.")
|
|
3368
|
-
typer.echo("Re-enable at any time:
|
|
3374
|
+
typer.echo("Re-enable at any time: sourcecode telemetry enable")
|
|
3369
3375
|
|
|
3370
3376
|
|
|
3371
3377
|
def _serialize_dict(data: dict, format: str) -> str:
|
|
@@ -3468,13 +3474,13 @@ def repo_ir_cmd(
|
|
|
3468
3474
|
|
|
3469
3475
|
\b
|
|
3470
3476
|
Examples:
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3477
|
+
sourcecode repo-ir
|
|
3478
|
+
sourcecode repo-ir /path/to/repo --since HEAD~1
|
|
3479
|
+
sourcecode repo-ir --files src/main/java/UserService.java
|
|
3480
|
+
sourcecode repo-ir --since main --output ir.json
|
|
3481
|
+
sourcecode repo-ir --since HEAD~3 --summary-only --output ir-small.json
|
|
3482
|
+
sourcecode repo-ir --max-nodes 200 --max-edges 500
|
|
3483
|
+
sourcecode repo-ir --output ir.json.gz --gzip
|
|
3478
3484
|
"""
|
|
3479
3485
|
import json as _json
|
|
3480
3486
|
|
|
@@ -3699,13 +3705,13 @@ def impact_cmd(
|
|
|
3699
3705
|
|
|
3700
3706
|
\b
|
|
3701
3707
|
NOTE: Free on repos up to the size limit; Pro unlocks enterprise-scale
|
|
3702
|
-
monoliths. Run '
|
|
3708
|
+
monoliths. Run 'sourcecode license' for details.
|
|
3703
3709
|
|
|
3704
3710
|
\b
|
|
3705
3711
|
Examples:
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3712
|
+
sourcecode impact UserService
|
|
3713
|
+
sourcecode impact org.keycloak.services.DefaultKeycloakSession /path/to/keycloak
|
|
3714
|
+
sourcecode impact UserService --depth 6 --output impact.json
|
|
3709
3715
|
"""
|
|
3710
3716
|
from sourcecode.license import require_repo_or_pro as _require_repo_or_pro
|
|
3711
3717
|
_require_repo_or_pro(str(path.resolve()), "impact")
|
|
@@ -3727,7 +3733,7 @@ def impact_cmd(
|
|
|
3727
3733
|
sys.stderr.write(
|
|
3728
3734
|
f"[impact] Legacy argument order detected: '{target}' is a directory, not a class name.\n"
|
|
3729
3735
|
f"[impact] Swapping: target='{path}', path='{target}'. "
|
|
3730
|
-
f"New syntax:
|
|
3736
|
+
f"New syntax: sourcecode impact <target> [path]\n"
|
|
3731
3737
|
)
|
|
3732
3738
|
sys.stderr.flush()
|
|
3733
3739
|
target, path = str(path), _target_as_path
|
|
@@ -3736,7 +3742,7 @@ def impact_cmd(
|
|
|
3736
3742
|
_emit_error_json(
|
|
3737
3743
|
INVALID_INPUT_CODE,
|
|
3738
3744
|
"Class name must not be empty.",
|
|
3739
|
-
hint="Pass a class name or FQN. Example:
|
|
3745
|
+
hint="Pass a class name or FQN. Example: sourcecode impact OrderService .",
|
|
3740
3746
|
expected="A non-empty class name or FQN.",
|
|
3741
3747
|
)
|
|
3742
3748
|
raise typer.Exit(1)
|
|
@@ -3749,7 +3755,7 @@ def impact_cmd(
|
|
|
3749
3755
|
path=str(root),
|
|
3750
3756
|
hint=(
|
|
3751
3757
|
"Pass an existing repository directory as the second argument. "
|
|
3752
|
-
"New syntax:
|
|
3758
|
+
"New syntax: sourcecode impact <target> [path] — "
|
|
3753
3759
|
"target is the class name, path is the repo root."
|
|
3754
3760
|
),
|
|
3755
3761
|
expected="A directory path.",
|
|
@@ -3890,13 +3896,13 @@ def endpoints_cmd(
|
|
|
3890
3896
|
|
|
3891
3897
|
\b
|
|
3892
3898
|
Examples:
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3899
|
+
sourcecode endpoints .
|
|
3900
|
+
sourcecode endpoints /path/to/repo
|
|
3901
|
+
sourcecode endpoints . --output endpoints.json
|
|
3902
|
+
sourcecode endpoints . --format yaml
|
|
3903
|
+
sourcecode endpoints . --path-prefix /v1/liquidacion
|
|
3904
|
+
sourcecode endpoints . --controller LiquidacionJornada
|
|
3905
|
+
sourcecode endpoints . --limit 10
|
|
3900
3906
|
"""
|
|
3901
3907
|
_enforce_format("endpoints", format)
|
|
3902
3908
|
|
|
@@ -4475,10 +4481,10 @@ def validation_cmd(
|
|
|
4475
4481
|
|
|
4476
4482
|
\b
|
|
4477
4483
|
Examples:
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4484
|
+
sourcecode validation .
|
|
4485
|
+
sourcecode validation . --gaps-only
|
|
4486
|
+
sourcecode validation . --path-prefix /owners
|
|
4487
|
+
sourcecode validation . --format yaml
|
|
4482
4488
|
"""
|
|
4483
4489
|
_enforce_format("validation", format)
|
|
4484
4490
|
|
|
@@ -4670,17 +4676,17 @@ def spring_audit_cmd(
|
|
|
4670
4676
|
|
|
4671
4677
|
\b
|
|
4672
4678
|
CI/CD usage:
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4679
|
+
sourcecode spring-audit . --ci # exit 1 on any finding
|
|
4680
|
+
sourcecode spring-audit . --ci --min-severity high # exit 1 only on high/critical
|
|
4681
|
+
sourcecode spring-audit . --ci --format github-comment # Markdown PR comment + exit 1
|
|
4676
4682
|
|
|
4677
4683
|
\b
|
|
4678
4684
|
Examples:
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4685
|
+
sourcecode spring-audit .
|
|
4686
|
+
sourcecode spring-audit /path/to/repo
|
|
4687
|
+
sourcecode spring-audit . --scope security
|
|
4688
|
+
sourcecode spring-audit . --min-severity high
|
|
4689
|
+
sourcecode spring-audit . --output audit.json
|
|
4684
4690
|
"""
|
|
4685
4691
|
import json as _json
|
|
4686
4692
|
|
|
@@ -4836,6 +4842,16 @@ def migrate_check_cmd(
|
|
|
4836
4842
|
help="Minimum severity to include: critical, high, medium, or low (default).",
|
|
4837
4843
|
show_default=True,
|
|
4838
4844
|
),
|
|
4845
|
+
compact: bool = typer.Option(
|
|
4846
|
+
False,
|
|
4847
|
+
"--compact",
|
|
4848
|
+
help=(
|
|
4849
|
+
"Bounded decision summary (JSON): readiness scores, headline blocker, "
|
|
4850
|
+
"effort, executive summary, Hibernate classification and aggregate "
|
|
4851
|
+
"counts, with large collections capped to their top items (+N more). "
|
|
4852
|
+
"No new analysis — same report, projected. Full detail is the default."
|
|
4853
|
+
),
|
|
4854
|
+
),
|
|
4839
4855
|
no_cache: bool = typer.Option(
|
|
4840
4856
|
False, "--no-cache",
|
|
4841
4857
|
help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
|
|
@@ -4866,10 +4882,11 @@ def migrate_check_cmd(
|
|
|
4866
4882
|
|
|
4867
4883
|
\b
|
|
4868
4884
|
Examples:
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4885
|
+
sourcecode migrate-check .
|
|
4886
|
+
sourcecode migrate-check . --compact bounded decision summary
|
|
4887
|
+
sourcecode migrate-check /path/to/repo --format text
|
|
4888
|
+
sourcecode migrate-check . --min-severity high
|
|
4889
|
+
sourcecode migrate-check . --output migration.json
|
|
4873
4890
|
"""
|
|
4874
4891
|
from sourcecode.repository_ir import find_java_files
|
|
4875
4892
|
from sourcecode.migrate_check import run_migrate_check
|
|
@@ -4905,7 +4922,11 @@ def migrate_check_cmd(
|
|
|
4905
4922
|
if format == "text":
|
|
4906
4923
|
output = report.to_text(min_severity=min_severity)
|
|
4907
4924
|
else:
|
|
4908
|
-
|
|
4925
|
+
# --compact projects the SAME report into a bounded, decision-grade view
|
|
4926
|
+
# (output-shaping only). Default (full) output is unchanged — it is the
|
|
4927
|
+
# baseline-oracle form and must stay byte-identical.
|
|
4928
|
+
payload = report.to_compact_dict() if compact else report.to_dict()
|
|
4929
|
+
output = _serialize_dict(payload, "json")
|
|
4909
4930
|
|
|
4910
4931
|
_total = report.summary.get("total_findings", 0)
|
|
4911
4932
|
_emit_command_output(
|
|
@@ -4990,9 +5011,9 @@ def impact_chain_cmd(
|
|
|
4990
5011
|
|
|
4991
5012
|
\b
|
|
4992
5013
|
Examples:
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
5014
|
+
sourcecode impact-chain OrderService .
|
|
5015
|
+
sourcecode impact-chain com.example.OrderService#placeOrder /path/to/repo
|
|
5016
|
+
sourcecode impact-chain PaymentService . --depth 6 --output impact.json
|
|
4996
5017
|
"""
|
|
4997
5018
|
import json as _json
|
|
4998
5019
|
|
|
@@ -5006,7 +5027,7 @@ def impact_chain_cmd(
|
|
|
5006
5027
|
_emit_error_json(
|
|
5007
5028
|
INVALID_INPUT_CODE,
|
|
5008
5029
|
"Symbol name must not be empty.",
|
|
5009
|
-
hint="Pass a class name or FQN. Example:
|
|
5030
|
+
hint="Pass a class name or FQN. Example: sourcecode impact-chain OrderService .",
|
|
5010
5031
|
expected="A non-empty class name or FQN.",
|
|
5011
5032
|
)
|
|
5012
5033
|
raise typer.Exit(code=1)
|
|
@@ -5134,9 +5155,9 @@ def pr_impact_cmd(
|
|
|
5134
5155
|
|
|
5135
5156
|
\b
|
|
5136
5157
|
Examples:
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5158
|
+
sourcecode pr-impact --files changed_files.txt
|
|
5159
|
+
sourcecode pr-impact /path/to/repo --files diff.txt --format json
|
|
5160
|
+
sourcecode pr-impact --files changes.txt --output pr_report.txt
|
|
5140
5161
|
"""
|
|
5141
5162
|
import json as _json
|
|
5142
5163
|
|
|
@@ -5163,7 +5184,7 @@ def pr_impact_cmd(
|
|
|
5163
5184
|
path=str(files),
|
|
5164
5185
|
hint=(
|
|
5165
5186
|
"Create a file with one changed Java file path per line, then pass it with --files. "
|
|
5166
|
-
"Example: git diff --name-only HEAD~1 > changed.txt &&
|
|
5187
|
+
"Example: git diff --name-only HEAD~1 > changed.txt && sourcecode pr-impact . --files changed.txt"
|
|
5167
5188
|
),
|
|
5168
5189
|
expected="A text file containing one Java file path per line.",
|
|
5169
5190
|
)
|
|
@@ -5270,9 +5291,9 @@ def explain_cmd(
|
|
|
5270
5291
|
|
|
5271
5292
|
\b
|
|
5272
5293
|
Examples:
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5294
|
+
sourcecode explain UserService
|
|
5295
|
+
sourcecode explain OrderController /path/to/repo
|
|
5296
|
+
sourcecode explain UserService --format json
|
|
5276
5297
|
"""
|
|
5277
5298
|
import json as _json
|
|
5278
5299
|
|
|
@@ -5285,7 +5306,7 @@ def explain_cmd(
|
|
|
5285
5306
|
_emit_error_json(
|
|
5286
5307
|
INVALID_INPUT_CODE,
|
|
5287
5308
|
"Class name must not be empty.",
|
|
5288
|
-
hint="Pass a class name. Example:
|
|
5309
|
+
hint="Pass a class name. Example: sourcecode explain UserService .",
|
|
5289
5310
|
expected="A non-empty class name.",
|
|
5290
5311
|
)
|
|
5291
5312
|
raise typer.Exit(code=1)
|
|
@@ -5317,7 +5338,7 @@ def explain_cmd(
|
|
|
5317
5338
|
_emit_error_json(
|
|
5318
5339
|
INVALID_INPUT_CODE,
|
|
5319
5340
|
f"No Java files found in '{target}'.",
|
|
5320
|
-
hint="
|
|
5341
|
+
hint="sourcecode explain requires a Java/Spring repository.",
|
|
5321
5342
|
expected="A directory containing .java source files.",
|
|
5322
5343
|
)
|
|
5323
5344
|
raise typer.Exit(code=1)
|
|
@@ -5378,16 +5399,16 @@ def onboard_cmd(
|
|
|
5378
5399
|
|
|
5379
5400
|
\b
|
|
5380
5401
|
Workflow:
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5402
|
+
sourcecode onboard .
|
|
5403
|
+
sourcecode onboard /path/to/repo --llm-prompt
|
|
5404
|
+
sourcecode onboard . --output onboard.json
|
|
5384
5405
|
|
|
5385
5406
|
\b
|
|
5386
5407
|
Related workflows:
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5408
|
+
sourcecode impact <target> — What breaks if I touch this?
|
|
5409
|
+
sourcecode review-pr — Risk-rank a pending PR
|
|
5410
|
+
sourcecode modernize . — Where should I refactor first?
|
|
5411
|
+
sourcecode fix-bug — Where does this symptom live?
|
|
5391
5412
|
"""
|
|
5392
5413
|
ctx.invoke(
|
|
5393
5414
|
prepare_context_cmd,
|
|
@@ -5447,14 +5468,14 @@ def review_pr_cmd(
|
|
|
5447
5468
|
|
|
5448
5469
|
\b
|
|
5449
5470
|
Workflow:
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5471
|
+
sourcecode review-pr --since origin/main
|
|
5472
|
+
sourcecode review-pr . --since main --format github-comment
|
|
5473
|
+
sourcecode review-pr . --since HEAD~3 --output review.json
|
|
5453
5474
|
|
|
5454
5475
|
\b
|
|
5455
5476
|
Related workflows:
|
|
5456
|
-
|
|
5457
|
-
|
|
5477
|
+
sourcecode impact <target> — Single-symbol blast radius
|
|
5478
|
+
sourcecode onboard . — Full architecture onboarding
|
|
5458
5479
|
"""
|
|
5459
5480
|
ctx.invoke(
|
|
5460
5481
|
prepare_context_cmd,
|
|
@@ -5507,14 +5528,14 @@ def fix_bug_cmd(
|
|
|
5507
5528
|
|
|
5508
5529
|
\b
|
|
5509
5530
|
Workflow:
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5531
|
+
sourcecode fix-bug --symptom "NullPointerException in UserService"
|
|
5532
|
+
sourcecode fix-bug . --symptom "401 on /api/orders"
|
|
5533
|
+
sourcecode fix-bug . --output bug-context.json
|
|
5513
5534
|
|
|
5514
5535
|
\b
|
|
5515
5536
|
Related workflows:
|
|
5516
|
-
|
|
5517
|
-
|
|
5537
|
+
sourcecode impact <target> — Propagate impact from a specific class
|
|
5538
|
+
sourcecode onboard . — Full architecture context first
|
|
5518
5539
|
"""
|
|
5519
5540
|
# Detect misuse: `fix-bug "symptom text" /path` — path arg looks like a symptom.
|
|
5520
5541
|
_path_str = str(path)
|
|
@@ -5526,7 +5547,7 @@ def fix_bug_cmd(
|
|
|
5526
5547
|
_emit_error_json(
|
|
5527
5548
|
INVALID_INPUT_CODE,
|
|
5528
5549
|
f"'{_path_str}' is not a valid directory. Did you mean to use --symptom?",
|
|
5529
|
-
hint=f"Use:
|
|
5550
|
+
hint=f"Use: sourcecode fix-bug . --symptom {_path_str!r}",
|
|
5530
5551
|
expected="A repository directory path as first argument.",
|
|
5531
5552
|
)
|
|
5532
5553
|
raise typer.Exit(code=1)
|
|
@@ -5676,13 +5697,13 @@ def modernize_cmd(
|
|
|
5676
5697
|
|
|
5677
5698
|
\b
|
|
5678
5699
|
Workflow:
|
|
5679
|
-
|
|
5680
|
-
|
|
5700
|
+
sourcecode modernize .
|
|
5701
|
+
sourcecode modernize /path/to/repo --output modernize.json
|
|
5681
5702
|
|
|
5682
5703
|
\b
|
|
5683
5704
|
Related workflows:
|
|
5684
|
-
|
|
5685
|
-
|
|
5705
|
+
sourcecode onboard . — Architecture overview first
|
|
5706
|
+
sourcecode impact <target> — Verify impact before touching a hotspot
|
|
5686
5707
|
"""
|
|
5687
5708
|
import json as _json
|
|
5688
5709
|
from sourcecode.repository_ir import build_repo_ir, find_java_files, apply_ir_size_limits
|
|
@@ -5927,7 +5948,7 @@ def modernize_cmd(
|
|
|
5927
5948
|
"in_degree = raw count of incoming graph edges across ALL edge types "
|
|
5928
5949
|
"(imports, injects, extends/implements, references, annotations), "
|
|
5929
5950
|
"counted at symbol level. This is deliberately larger than and NOT "
|
|
5930
|
-
"equal to `
|
|
5951
|
+
"equal to `sourcecode explain`'s caller count, which reports DISTINCT "
|
|
5931
5952
|
"dependent classes (DI dependents + reverse-call-graph callers, "
|
|
5932
5953
|
"deduplicated to class level). Use in_degree for blast-radius ranking, "
|
|
5933
5954
|
"explain's caller list for the concrete dependents to inspect."
|
|
@@ -6040,10 +6061,10 @@ def rename_class_cmd(
|
|
|
6040
6061
|
|
|
6041
6062
|
\b
|
|
6042
6063
|
Examples:
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6064
|
+
sourcecode rename-class . --from ServiceA --to ServiceB
|
|
6065
|
+
sourcecode rename-class /path/to/repo --from OrderManager --to OrderService
|
|
6066
|
+
sourcecode rename-class . --from OldName --to NewName --dry-run
|
|
6067
|
+
sourcecode rename-class . --from OldName --to NewName --output rename-audit.json
|
|
6047
6068
|
"""
|
|
6048
6069
|
import json as _json
|
|
6049
6070
|
from sourcecode.rename_refactor import rename_class
|
|
@@ -6146,10 +6167,10 @@ def chunk_file_cmd(
|
|
|
6146
6167
|
|
|
6147
6168
|
\b
|
|
6148
6169
|
Examples:
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6170
|
+
sourcecode chunk-file NominasCalculoService.java
|
|
6171
|
+
sourcecode chunk-file BigService.java --max-lines 300
|
|
6172
|
+
sourcecode chunk-file BigService.java --chunk 5 # read chunk 5 only
|
|
6173
|
+
sourcecode chunk-file BigService.java --metadata-only # sizes/boundaries only
|
|
6153
6174
|
"""
|
|
6154
6175
|
import json as _json
|
|
6155
6176
|
from sourcecode.file_chunker import chunk_java_file
|
|
@@ -6213,7 +6234,7 @@ def activate_cmd(
|
|
|
6213
6234
|
|
|
6214
6235
|
\b
|
|
6215
6236
|
Examples:
|
|
6216
|
-
|
|
6237
|
+
sourcecode activate SC-XXXX-XXXX-XXXX
|
|
6217
6238
|
"""
|
|
6218
6239
|
from sourcecode.license import activate_license as _activate
|
|
6219
6240
|
_activate(license_key)
|
|
@@ -6279,7 +6300,7 @@ def version_cmd() -> None:
|
|
|
6279
6300
|
"compatibility_schema_version": "1.0"}
|
|
6280
6301
|
"""
|
|
6281
6302
|
if getattr(sys.stdout, "isatty", lambda: False)():
|
|
6282
|
-
typer.echo(f"
|
|
6303
|
+
typer.echo(f"sourcecode {__version__}")
|
|
6283
6304
|
else:
|
|
6284
6305
|
import json as _json_ver
|
|
6285
6306
|
typer.echo(_json_ver.dumps({
|
|
@@ -6295,14 +6316,14 @@ def version_cmd() -> None:
|
|
|
6295
6316
|
def config_cmd() -> None:
|
|
6296
6317
|
"""Show current configuration."""
|
|
6297
6318
|
from sourcecode.telemetry.config import config_file_path, is_enabled
|
|
6298
|
-
typer.echo(f"
|
|
6319
|
+
typer.echo(f"sourcecode {__version__}")
|
|
6299
6320
|
typer.echo(f"Config: {config_file_path()}")
|
|
6300
6321
|
typer.echo(f"Telemetry: {'enabled' if is_enabled() else 'disabled'}")
|
|
6301
6322
|
typer.echo("")
|
|
6302
6323
|
typer.echo("Manage telemetry:")
|
|
6303
|
-
typer.echo("
|
|
6304
|
-
typer.echo("
|
|
6305
|
-
typer.echo("
|
|
6324
|
+
typer.echo(" sourcecode telemetry enable")
|
|
6325
|
+
typer.echo(" sourcecode telemetry disable")
|
|
6326
|
+
typer.echo(" sourcecode telemetry status")
|
|
6306
6327
|
|
|
6307
6328
|
|
|
6308
6329
|
# ── cold-start (RIS bootstrap for external MCP and agents) ───────────────────
|
|
@@ -6346,7 +6367,7 @@ def cold_start_cmd(
|
|
|
6346
6367
|
if isinstance(result.get("endpoints"), list):
|
|
6347
6368
|
result["endpoints"] = result["endpoints"][:30]
|
|
6348
6369
|
result["_meta"] = {**(result.get("_meta") or {}), "compact_mode": True,
|
|
6349
|
-
"full_available": "
|
|
6370
|
+
"full_available": "sourcecode cold-start (without --compact)"}
|
|
6350
6371
|
_out = _json.dumps(result, indent=2, ensure_ascii=False)
|
|
6351
6372
|
_size = len(_out.encode("utf-8"))
|
|
6352
6373
|
_tokens = _size // 4
|
|
@@ -6437,13 +6458,13 @@ def mcp_init(
|
|
|
6437
6458
|
|
|
6438
6459
|
\b
|
|
6439
6460
|
Detects installed MCP clients, backs up their config files, and safely
|
|
6440
|
-
inserts the
|
|
6461
|
+
inserts the sourcecode server entry. Fully idempotent — safe to re-run.
|
|
6441
6462
|
|
|
6442
6463
|
\b
|
|
6443
6464
|
Examples:
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6465
|
+
sourcecode mcp init
|
|
6466
|
+
sourcecode mcp init --target claude-desktop
|
|
6467
|
+
sourcecode mcp init --target cursor --yes
|
|
6447
6468
|
"""
|
|
6448
6469
|
from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
|
|
6449
6470
|
from sourcecode.mcp.onboarding.planner import build_install_plan
|
|
@@ -6488,7 +6509,7 @@ def mcp_init(
|
|
|
6488
6509
|
for a in already_done:
|
|
6489
6510
|
typer.echo(f" ✓ {a.client.name} {a.client.config_path}")
|
|
6490
6511
|
typer.echo("")
|
|
6491
|
-
typer.echo("Nothing to do. Remove:
|
|
6512
|
+
typer.echo("Nothing to do. Remove: sourcecode mcp remove")
|
|
6492
6513
|
raise typer.Exit(code=0)
|
|
6493
6514
|
|
|
6494
6515
|
if already_done:
|
|
@@ -6545,7 +6566,7 @@ def mcp_init(
|
|
|
6545
6566
|
if not is_client_running(a.client):
|
|
6546
6567
|
typer.echo(
|
|
6547
6568
|
f" ⚠ Config written but {a.client.name} is not running. "
|
|
6548
|
-
f"Start {a.client.name} and run
|
|
6569
|
+
f"Start {a.client.name} and run sourcecode mcp status to verify.",
|
|
6549
6570
|
err=False,
|
|
6550
6571
|
)
|
|
6551
6572
|
else:
|
|
@@ -6553,7 +6574,7 @@ def mcp_init(
|
|
|
6553
6574
|
typer.echo(f" ✓ {a.client.name} is running.{restart_msg}")
|
|
6554
6575
|
|
|
6555
6576
|
typer.echo("")
|
|
6556
|
-
typer.echo(" Remove:
|
|
6577
|
+
typer.echo(" Remove: sourcecode mcp remove")
|
|
6557
6578
|
|
|
6558
6579
|
# Clear nudge flag: next run finds is_installed=True → no nudge.
|
|
6559
6580
|
from sourcecode.mcp_nudge import clear_nudge_flag as _clear_nudge
|
|
@@ -6590,18 +6611,18 @@ def mcp_status() -> None:
|
|
|
6590
6611
|
if not clients:
|
|
6591
6612
|
typer.echo(" No MCP clients detected on this system.")
|
|
6592
6613
|
typer.echo(sep)
|
|
6593
|
-
typer.echo(" Setup:
|
|
6614
|
+
typer.echo(" Setup: sourcecode mcp init")
|
|
6594
6615
|
raise typer.Exit(code=0)
|
|
6595
6616
|
|
|
6596
6617
|
# Stage 2: Config files — is sourcecode registered in the client's config?
|
|
6597
6618
|
# FIX-P0-6: "configured" and "running" are distinct, independent checks.
|
|
6598
6619
|
# Also detect external server installs (different Python/executable than CLI).
|
|
6599
|
-
typer.echo("Config (
|
|
6620
|
+
typer.echo("Config (sourcecode registered in client config?)")
|
|
6600
6621
|
for client in clients:
|
|
6601
6622
|
if not client.app_installed:
|
|
6602
6623
|
typer.echo(f" {client.name:<20} ✗ app not found at expected path")
|
|
6603
6624
|
typer.echo(f" Expected: {client.config_path}")
|
|
6604
|
-
typer.echo(f" Fix:
|
|
6625
|
+
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
6605
6626
|
continue
|
|
6606
6627
|
config = applier.read_config(client.config_path)
|
|
6607
6628
|
if applier.is_installed(config):
|
|
@@ -6617,11 +6638,11 @@ def mcp_status() -> None:
|
|
|
6617
6638
|
or (_reg_args and _reg_args[:2] == ["mcp", "serve"])
|
|
6618
6639
|
)
|
|
6619
6640
|
if _is_builtin:
|
|
6620
|
-
typer.echo(f" Server: built-in (
|
|
6641
|
+
typer.echo(f" Server: built-in (sourcecode mcp serve) version={_cli_version}")
|
|
6621
6642
|
else:
|
|
6622
6643
|
# External server — different Python or custom server.py
|
|
6623
6644
|
typer.echo(f" Server: ⚠ EXTERNAL — {_reg_cmd} {' '.join(_reg_args)}")
|
|
6624
|
-
# Try to get the external server's
|
|
6645
|
+
# Try to get the external server's sourcecode version by finding the
|
|
6625
6646
|
# sourcecode binary relative to the registered Python executable,
|
|
6626
6647
|
# or falling back to probing the Python for the installed package.
|
|
6627
6648
|
_ext_ver: str = "unknown"
|
|
@@ -6655,18 +6676,18 @@ def mcp_status() -> None:
|
|
|
6655
6676
|
f"CLI version={_cli_version}"
|
|
6656
6677
|
)
|
|
6657
6678
|
typer.echo(
|
|
6658
|
-
" To fix:
|
|
6679
|
+
" To fix: sourcecode mcp init (re-register using CLI built-in server)"
|
|
6659
6680
|
)
|
|
6660
6681
|
elif _ext_ver != "unknown":
|
|
6661
6682
|
typer.echo(f" External server version={_ext_ver} (matches CLI ✓)")
|
|
6662
6683
|
else:
|
|
6663
6684
|
typer.echo(
|
|
6664
6685
|
f" ⚠ Cannot verify external server version (CLI={_cli_version}). "
|
|
6665
|
-
"Re-run:
|
|
6686
|
+
"Re-run: sourcecode mcp init to switch to built-in server."
|
|
6666
6687
|
)
|
|
6667
6688
|
else:
|
|
6668
|
-
typer.echo(f" {client.name:<20} ✗ not configured (app found, but
|
|
6669
|
-
typer.echo(f" Fix:
|
|
6689
|
+
typer.echo(f" {client.name:<20} ✗ not configured (app found, but sourcecode entry missing)")
|
|
6690
|
+
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
6670
6691
|
typer.echo("")
|
|
6671
6692
|
|
|
6672
6693
|
# Build config state map for cross-check in Stage 3.
|
|
@@ -6694,19 +6715,19 @@ def mcp_status() -> None:
|
|
|
6694
6715
|
_action_required.append(client.name)
|
|
6695
6716
|
else:
|
|
6696
6717
|
typer.echo(f" {client.name:<20} ✗ not running")
|
|
6697
|
-
typer.echo(f" Fix: open {client.name}, then run
|
|
6718
|
+
typer.echo(f" Fix: open {client.name}, then run sourcecode mcp status")
|
|
6698
6719
|
|
|
6699
6720
|
typer.echo(sep)
|
|
6700
6721
|
if _action_required:
|
|
6701
6722
|
for _name in _action_required:
|
|
6702
|
-
typer.echo(f" ⚠ ACTION REQUIRED: {_name} is running but
|
|
6703
|
-
typer.echo(" Run:
|
|
6723
|
+
typer.echo(f" ⚠ ACTION REQUIRED: {_name} is running but sourcecode is not configured.")
|
|
6724
|
+
typer.echo(" Run: sourcecode mcp init")
|
|
6704
6725
|
typer.echo("")
|
|
6705
6726
|
typer.echo(" Note: 'configured' and 'running' are checked independently.")
|
|
6706
6727
|
typer.echo(" A running app still needs restart after first-time config.")
|
|
6707
6728
|
typer.echo(" Path: repo_path must use forward slashes: C:/Users/... or /unix/path")
|
|
6708
|
-
typer.echo(" Setup:
|
|
6709
|
-
typer.echo(" Remove:
|
|
6729
|
+
typer.echo(" Setup: sourcecode mcp init")
|
|
6730
|
+
typer.echo(" Remove: sourcecode mcp remove")
|
|
6710
6731
|
|
|
6711
6732
|
|
|
6712
6733
|
@mcp_app.command("remove")
|
|
@@ -6717,7 +6738,7 @@ def mcp_remove(
|
|
|
6717
6738
|
|
|
6718
6739
|
\b
|
|
6719
6740
|
Backs up config files before modifying. Restores from backup when available,
|
|
6720
|
-
otherwise removes the
|
|
6741
|
+
otherwise removes the sourcecode entry while preserving all other config.
|
|
6721
6742
|
"""
|
|
6722
6743
|
from sourcecode.mcp.onboarding.detector import detect_clients
|
|
6723
6744
|
from sourcecode.mcp.onboarding.planner import build_remove_plan
|
|
@@ -6729,7 +6750,7 @@ def mcp_remove(
|
|
|
6729
6750
|
|
|
6730
6751
|
if not installed:
|
|
6731
6752
|
typer.echo("sourcecode MCP integration not found in any client config.")
|
|
6732
|
-
typer.echo(" Setup:
|
|
6753
|
+
typer.echo(" Setup: sourcecode mcp init")
|
|
6733
6754
|
raise typer.Exit(code=0)
|
|
6734
6755
|
|
|
6735
6756
|
typer.echo("Remove sourcecode MCP integration from:")
|
|
@@ -6772,23 +6793,23 @@ def mcp_remove(
|
|
|
6772
6793
|
raise typer.Exit(code=1)
|
|
6773
6794
|
|
|
6774
6795
|
typer.echo("MCP integration removed.")
|
|
6775
|
-
typer.echo(" Re-add:
|
|
6796
|
+
typer.echo(" Re-add: sourcecode mcp init")
|
|
6776
6797
|
|
|
6777
6798
|
|
|
6778
6799
|
@mcp_app.command("list-tools")
|
|
6779
6800
|
def mcp_list_tools(
|
|
6780
6801
|
json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
|
|
6781
6802
|
) -> None:
|
|
6782
|
-
"""List all MCP tools exposed by the
|
|
6803
|
+
"""List all MCP tools exposed by the sourcecode server.
|
|
6783
6804
|
|
|
6784
6805
|
\b
|
|
6785
6806
|
Shows each tool name, its description, and the CLI command it maps to.
|
|
6786
|
-
Useful for discovering capabilities when using
|
|
6807
|
+
Useful for discovering capabilities when using sourcecode as an MCP server.
|
|
6787
6808
|
|
|
6788
6809
|
\b
|
|
6789
6810
|
Examples:
|
|
6790
|
-
|
|
6791
|
-
|
|
6811
|
+
sourcecode mcp list-tools
|
|
6812
|
+
sourcecode mcp list-tools --json
|
|
6792
6813
|
"""
|
|
6793
6814
|
import asyncio
|
|
6794
6815
|
import json as _json
|
|
@@ -6811,8 +6832,8 @@ def mcp_list_tools(
|
|
|
6811
6832
|
desc_first_line = (t.description or "").strip().splitlines()[0] if t.description else ""
|
|
6812
6833
|
typer.echo(f" {t.name:<35} {desc_first_line}")
|
|
6813
6834
|
typer.echo("")
|
|
6814
|
-
typer.echo("Use:
|
|
6815
|
-
typer.echo("Use:
|
|
6835
|
+
typer.echo("Use: sourcecode mcp serve — start MCP server on stdio")
|
|
6836
|
+
typer.echo("Use: sourcecode mcp init — configure MCP client")
|
|
6816
6837
|
|
|
6817
6838
|
|
|
6818
6839
|
# ── Cache subcommands ─────────────────────────────────────────────────────────
|
|
@@ -7013,18 +7034,8 @@ def cache_freshness_cmd(
|
|
|
7013
7034
|
|
|
7014
7035
|
# ── Entry point ───────────────────────────────────────────────────────────────
|
|
7015
7036
|
|
|
7016
|
-
def _stderr_is_interactive() -> bool:
|
|
7017
|
-
"""True when stderr is a human terminal — used to gate the alias deprecation
|
|
7018
|
-
line so non-interactive callers keep a clean (JSON-only) stderr."""
|
|
7019
|
-
try:
|
|
7020
|
-
return bool(sys.stderr.isatty())
|
|
7021
|
-
except Exception:
|
|
7022
|
-
return False
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
7037
|
def main_entry() -> None:
|
|
7026
|
-
"""CLI entry point
|
|
7027
|
-
``sourcecode`` compat alias — one implementation, no duplication.
|
|
7038
|
+
"""CLI entry point.
|
|
7028
7039
|
|
|
7029
7040
|
Calls _preprocess_argv() before Typer/Click parses sys.argv so that
|
|
7030
7041
|
repository path tokens are extracted before Click's Group callback
|
|
@@ -7038,23 +7049,9 @@ def main_entry() -> None:
|
|
|
7038
7049
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
7039
7050
|
except Exception:
|
|
7040
7051
|
pass
|
|
7041
|
-
# Deprecation notice when invoked through the legacy `sourcecode` alias.
|
|
7042
|
-
# One line, and only on an interactive terminal — pipes/agents that still call
|
|
7043
|
-
# `sourcecode` keep clean stderr (error envelopes are JSON on stderr), so the
|
|
7044
|
-
# machine contract is untouched. Humans at a TTY get nudged toward `ask`.
|
|
7045
|
-
try:
|
|
7046
|
-
_invoked = os.path.basename(sys.argv[0] or "").lower()
|
|
7047
|
-
if _invoked.startswith("sourcecode") and _stderr_is_interactive():
|
|
7048
|
-
sys.stderr.write(
|
|
7049
|
-
"⚠ 'sourcecode' is a deprecated alias for 'ask' and will be removed "
|
|
7050
|
-
"in a future major release. Use 'ask' instead.\n"
|
|
7051
|
-
)
|
|
7052
|
-
except Exception:
|
|
7053
|
-
pass
|
|
7054
7052
|
_preprocess_argv()
|
|
7055
7053
|
try:
|
|
7056
|
-
|
|
7057
|
-
app(prog_name="ask")
|
|
7054
|
+
app()
|
|
7058
7055
|
finally:
|
|
7059
7056
|
# Best-effort "new version available" nudge. Only speaks on an
|
|
7060
7057
|
# interactive terminal; never blocks, raises, or affects exit status.
|