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/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]ask activate <key>[/dim] to unlock Pro"
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: ask[/dim] {plan_badge}
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
- ask --compact high-signal summary (~2,500–4,000 tokens)
168
- ask --compact --git-context include git hotspots and uncommitted files
169
- ask --agent full structured JSON for AI agents
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
- ask my-project --compact
182
- ask . --compact --git-context --copy
183
- ask . --changed-only --git-context
184
- ask prepare-context onboard my-project
185
- ask prepare-context delta . --since main
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]→ ask activate <key>[/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="ask",
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(" → ask mcp init", err=True)
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"ask {__version__}")
676
+ typer.echo(f"sourcecode {__version__}")
678
677
  raise typer.Exit()
679
678
 
680
679
 
681
- # ANSI Shadow block wordmark: large "ASK" with a spaced "ENGINE" label beneath
682
- # the product identity (ASK Engine), not the legacy "sourcecode".
683
- _WELCOME_ASK = (
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
- ("ask --compact", "repo summary"),
695
- ("ask migrate-check", "upgrade readiness (free)"),
696
- ("ask prepare-context onboard", "onboarding"),
697
- ("ask mcp init", "connect IDE"),
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: ask", "",
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(" ask --help all commands")
714
+ lines.append(" sourcecode --help all commands")
709
715
  if tier != "Pro":
710
- lines.append(" ask activate <key> unlock Pro")
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
- ask_w = max(len(s) for s in _WELCOME_ASK)
744
+ code_w = max(len(s) for s in _WELCOME_CODE)
739
745
 
740
746
  t = Text()
741
- # Large "ASK" wordmark; version + tier ride the right of its middle rows.
742
- for i, ln in enumerate(_WELCOME_ASK):
743
- t.append(ln.ljust(ask_w), style="bold cyan")
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
- ask --compact high-signal summary (recommended)
1014
- ask --compact --git-context include git hotspots
1015
- ask /path/to/repo --compact analyze specific path
1016
- ask --agent agent-optimized output (full detail)
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
- ask prepare-context explain
2775
- ask prepare-context explain /path/to/repo
2776
- ask prepare-context fix-bug
2777
- ask prepare-context delta --since main
2778
- ask prepare-context review-pr --since origin/main
2779
- ask prepare-context review-pr . --since main --output review.json
2780
- ask prepare-context onboard --llm-prompt
2781
- ask prepare-context --task-help
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": "ask prepare-context review-pr --since <ref>",
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: ask telemetry 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: ask telemetry disable")
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: ask telemetry enable")
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
- ask repo-ir
3472
- ask repo-ir /path/to/repo --since HEAD~1
3473
- ask repo-ir --files src/main/java/UserService.java
3474
- ask repo-ir --since main --output ir.json
3475
- ask repo-ir --since HEAD~3 --summary-only --output ir-small.json
3476
- ask repo-ir --max-nodes 200 --max-edges 500
3477
- ask repo-ir --output ir.json.gz --gzip
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 'ask license' for details.
3708
+ monoliths. Run 'sourcecode license' for details.
3703
3709
 
3704
3710
  \b
3705
3711
  Examples:
3706
- ask impact UserService
3707
- ask impact org.keycloak.services.DefaultKeycloakSession /path/to/keycloak
3708
- ask impact UserService --depth 6 --output impact.json
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: ask impact <target> [path]\n"
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: ask impact OrderService .",
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: ask impact <target> [path] — "
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
- ask endpoints .
3894
- ask endpoints /path/to/repo
3895
- ask endpoints . --output endpoints.json
3896
- ask endpoints . --format yaml
3897
- ask endpoints . --path-prefix /v1/liquidacion
3898
- ask endpoints . --controller LiquidacionJornada
3899
- ask endpoints . --limit 10
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
- ask validation .
4479
- ask validation . --gaps-only
4480
- ask validation . --path-prefix /owners
4481
- ask validation . --format yaml
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
- ask spring-audit . --ci # exit 1 on any finding
4674
- ask spring-audit . --ci --min-severity high # exit 1 only on high/critical
4675
- ask spring-audit . --ci --format github-comment # Markdown PR comment + exit 1
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
- ask spring-audit .
4680
- ask spring-audit /path/to/repo
4681
- ask spring-audit . --scope security
4682
- ask spring-audit . --min-severity high
4683
- ask spring-audit . --output audit.json
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
- ask migrate-check .
4870
- ask migrate-check /path/to/repo --format text
4871
- ask migrate-check . --min-severity high
4872
- ask migrate-check . --output migration.json
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
- output = _serialize_dict(report.to_dict(), "json")
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
- ask impact-chain OrderService .
4994
- ask impact-chain com.example.OrderService#placeOrder /path/to/repo
4995
- ask impact-chain PaymentService . --depth 6 --output impact.json
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: ask impact-chain OrderService .",
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
- ask pr-impact --files changed_files.txt
5138
- ask pr-impact /path/to/repo --files diff.txt --format json
5139
- ask pr-impact --files changes.txt --output pr_report.txt
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 && ask pr-impact . --files 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
- ask explain UserService
5274
- ask explain OrderController /path/to/repo
5275
- ask explain UserService --format json
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: ask explain UserService .",
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="ask explain requires a Java/Spring repository.",
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
- ask onboard .
5382
- ask onboard /path/to/repo --llm-prompt
5383
- ask onboard . --output onboard.json
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
- ask impact <target> — What breaks if I touch this?
5388
- ask review-pr — Risk-rank a pending PR
5389
- ask modernize . — Where should I refactor first?
5390
- ask fix-bug — Where does this symptom live?
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
- ask review-pr --since origin/main
5451
- ask review-pr . --since main --format github-comment
5452
- ask review-pr . --since HEAD~3 --output review.json
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
- ask impact <target> — Single-symbol blast radius
5457
- ask onboard . — Full architecture onboarding
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
- ask fix-bug --symptom "NullPointerException in UserService"
5511
- ask fix-bug . --symptom "401 on /api/orders"
5512
- ask fix-bug . --output bug-context.json
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
- ask impact <target> — Propagate impact from a specific class
5517
- ask onboard . — Full architecture context first
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: ask fix-bug . --symptom {_path_str!r}",
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
- ask modernize .
5680
- ask modernize /path/to/repo --output modernize.json
5700
+ sourcecode modernize .
5701
+ sourcecode modernize /path/to/repo --output modernize.json
5681
5702
 
5682
5703
  \b
5683
5704
  Related workflows:
5684
- ask onboard . — Architecture overview first
5685
- ask impact <target> — Verify impact before touching a hotspot
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 `ask explain`'s caller count, which reports DISTINCT "
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
- ask rename-class . --from ServiceA --to ServiceB
6044
- ask rename-class /path/to/repo --from OrderManager --to OrderService
6045
- ask rename-class . --from OldName --to NewName --dry-run
6046
- ask rename-class . --from OldName --to NewName --output rename-audit.json
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
- ask chunk-file NominasCalculoService.java
6150
- ask chunk-file BigService.java --max-lines 300
6151
- ask chunk-file BigService.java --chunk 5 # read chunk 5 only
6152
- ask chunk-file BigService.java --metadata-only # sizes/boundaries only
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
- ask activate SC-XXXX-XXXX-XXXX
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"ask {__version__}")
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"ask {__version__}")
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(" ask telemetry enable")
6304
- typer.echo(" ask telemetry disable")
6305
- typer.echo(" ask telemetry status")
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": "ask cold-start (without --compact)"}
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 ASK Engine server entry. Fully idempotent — safe to re-run.
6461
+ inserts the sourcecode server entry. Fully idempotent — safe to re-run.
6441
6462
 
6442
6463
  \b
6443
6464
  Examples:
6444
- ask mcp init
6445
- ask mcp init --target claude-desktop
6446
- ask mcp init --target cursor --yes
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: ask mcp 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 ask mcp status to verify.",
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: ask mcp 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: ask mcp init")
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 (ASK Engine registered in client 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: ask mcp init --target {client.slug}")
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 (ask mcp serve) version={_cli_version}")
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 ask version by finding the
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: ask mcp init (re-register using CLI built-in server)"
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: ask mcp init to switch to built-in server."
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 ASK Engine entry missing)")
6669
- typer.echo(f" Fix: ask mcp init --target {client.slug}")
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 ask mcp status")
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 ASK Engine is not configured.")
6703
- typer.echo(" Run: ask mcp init")
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: ask mcp init")
6709
- typer.echo(" Remove: ask mcp 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 ASK Engine entry while preserving all other config.
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: ask mcp init")
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: ask mcp init")
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 ASK Engine MCP server.
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 ASK Engine as an MCP server.
6807
+ Useful for discovering capabilities when using sourcecode as an MCP server.
6787
6808
 
6788
6809
  \b
6789
6810
  Examples:
6790
- ask mcp list-tools
6791
- ask mcp list-tools --json
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: ask mcp serve — start MCP server on stdio")
6815
- typer.echo("Use: ask mcp init — configure MCP client")
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 for both the canonical ``ask`` command and the deprecated
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
- # prog_name pins usage/help to the canonical `ask`, whatever the alias.
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.