codeshift 0.3.7__py3-none-any.whl → 0.5.0__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.
Files changed (44) hide show
  1. codeshift/__init__.py +2 -2
  2. codeshift/cli/__init__.py +1 -1
  3. codeshift/cli/commands/__init__.py +1 -1
  4. codeshift/cli/commands/auth.py +46 -30
  5. codeshift/cli/commands/scan.py +2 -5
  6. codeshift/cli/commands/upgrade.py +69 -61
  7. codeshift/cli/commands/upgrade_all.py +1 -1
  8. codeshift/cli/main.py +2 -2
  9. codeshift/knowledge/generator.py +6 -0
  10. codeshift/knowledge_base/libraries/aiohttp.yaml +3 -3
  11. codeshift/knowledge_base/libraries/httpx.yaml +4 -4
  12. codeshift/knowledge_base/libraries/pytest.yaml +1 -1
  13. codeshift/knowledge_base/models.py +1 -0
  14. codeshift/migrator/llm_migrator.py +8 -12
  15. codeshift/migrator/transforms/marshmallow_transformer.py +50 -0
  16. codeshift/migrator/transforms/pydantic_v1_to_v2.py +191 -22
  17. codeshift/scanner/code_scanner.py +22 -2
  18. codeshift/utils/__init__.py +1 -1
  19. codeshift/utils/api_client.py +155 -15
  20. codeshift/utils/cache.py +1 -1
  21. codeshift/utils/credential_store.py +393 -0
  22. codeshift/utils/llm_client.py +111 -9
  23. {codeshift-0.3.7.dist-info → codeshift-0.5.0.dist-info}/METADATA +4 -16
  24. {codeshift-0.3.7.dist-info → codeshift-0.5.0.dist-info}/RECORD +28 -43
  25. {codeshift-0.3.7.dist-info → codeshift-0.5.0.dist-info}/licenses/LICENSE +1 -1
  26. codeshift/api/__init__.py +0 -1
  27. codeshift/api/auth.py +0 -182
  28. codeshift/api/config.py +0 -73
  29. codeshift/api/database.py +0 -215
  30. codeshift/api/main.py +0 -103
  31. codeshift/api/models/__init__.py +0 -55
  32. codeshift/api/models/auth.py +0 -108
  33. codeshift/api/models/billing.py +0 -92
  34. codeshift/api/models/migrate.py +0 -42
  35. codeshift/api/models/usage.py +0 -116
  36. codeshift/api/routers/__init__.py +0 -5
  37. codeshift/api/routers/auth.py +0 -440
  38. codeshift/api/routers/billing.py +0 -395
  39. codeshift/api/routers/migrate.py +0 -304
  40. codeshift/api/routers/usage.py +0 -291
  41. codeshift/api/routers/webhooks.py +0 -289
  42. {codeshift-0.3.7.dist-info → codeshift-0.5.0.dist-info}/WHEEL +0 -0
  43. {codeshift-0.3.7.dist-info → codeshift-0.5.0.dist-info}/entry_points.txt +0 -0
  44. {codeshift-0.3.7.dist-info → codeshift-0.5.0.dist-info}/top_level.txt +0 -0
codeshift/__init__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """
2
- PyResolve - AI-powered CLI tool for migrating Python code to handle breaking dependency changes.
2
+ Codeshift - AI-powered CLI tool for migrating Python code to handle breaking dependency changes.
3
3
 
4
4
  Don't just flag the update. Fix the break.
5
5
  """
6
6
 
7
7
  __version__ = "0.2.0"
8
- __author__ = "PyResolve Team"
8
+ __author__ = "Codeshift Team"
codeshift/cli/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- """CLI module for PyResolve."""
1
+ """CLI module for Codeshift."""
2
2
 
3
3
  from codeshift.cli.main import cli
4
4
 
@@ -1,4 +1,4 @@
1
- """CLI commands for PyResolve."""
1
+ """CLI commands for Codeshift."""
2
2
 
3
3
  from codeshift.cli.commands.apply import apply
4
4
  from codeshift.cli.commands.diff import diff
@@ -1,11 +1,10 @@
1
1
  """Authentication commands for Codeshift CLI."""
2
2
 
3
- import json
4
3
  import os
5
4
  import time
6
5
  import webbrowser
7
6
  from pathlib import Path
8
- from typing import Any, cast
7
+ from typing import Any
9
8
 
10
9
  import click
11
10
  import httpx
@@ -15,11 +14,16 @@ from rich.progress import Progress, SpinnerColumn, TextColumn
15
14
  from rich.prompt import Confirm, Prompt
16
15
  from rich.table import Table
17
16
 
17
+ from codeshift.utils.credential_store import (
18
+ CredentialDecryptionError,
19
+ get_credential_store,
20
+ )
21
+
18
22
  console = Console()
19
23
 
20
- # Config directory for storing credentials
24
+ # Config directory for storing credentials (kept for backward compatibility reference)
21
25
  CONFIG_DIR = Path.home() / ".config" / "codeshift"
22
- CREDENTIALS_FILE = CONFIG_DIR / "credentials.json"
26
+ CREDENTIALS_FILE = CONFIG_DIR / "credentials.json" # Legacy path
23
27
 
24
28
 
25
29
  def get_api_url() -> str:
@@ -28,34 +32,44 @@ def get_api_url() -> str:
28
32
 
29
33
 
30
34
  def load_credentials() -> dict[str, Any] | None:
31
- """Load saved credentials from disk."""
32
- if not CREDENTIALS_FILE.exists():
33
- return None
35
+ """Load saved credentials from secure storage.
36
+
37
+ Automatically handles migration from plaintext to encrypted storage.
38
+
39
+ Returns:
40
+ Dictionary of credentials, or None if not found.
41
+ """
42
+ store = get_credential_store()
34
43
  try:
35
- return cast(dict[str, Any], json.loads(CREDENTIALS_FILE.read_text()))
36
- except (OSError, json.JSONDecodeError):
44
+ return store.load()
45
+ except CredentialDecryptionError as e:
46
+ console.print(
47
+ Panel(
48
+ f"[red]Could not decrypt credentials:[/] {e}\n\n"
49
+ "This may happen if credentials were created on a different machine.\n"
50
+ "Please run [cyan]codeshift login[/] to re-authenticate.",
51
+ title="Credential Error",
52
+ )
53
+ )
37
54
  return None
38
55
 
39
56
 
40
57
  def save_credentials(credentials: dict) -> None:
41
- """Save credentials to disk."""
42
- CONFIG_DIR.mkdir(parents=True, exist_ok=True)
43
-
44
- # Set restrictive permissions
45
- CREDENTIALS_FILE.write_text(json.dumps(credentials, indent=2))
46
- os.chmod(CREDENTIALS_FILE, 0o600)
58
+ """Save credentials to secure encrypted storage."""
59
+ store = get_credential_store()
60
+ store.save(credentials)
47
61
 
48
62
 
49
63
  def delete_credentials() -> None:
50
- """Delete saved credentials."""
51
- if CREDENTIALS_FILE.exists():
52
- CREDENTIALS_FILE.unlink()
64
+ """Delete saved credentials securely."""
65
+ store = get_credential_store()
66
+ store.delete()
53
67
 
54
68
 
55
69
  def get_api_key() -> str | None:
56
70
  """Get API key from environment or saved credentials."""
57
71
  # Check environment first
58
- api_key = os.environ.get("PYRESOLVE_API_KEY")
72
+ api_key = os.environ.get("CODESHIFT_API_KEY")
59
73
  if api_key:
60
74
  return api_key
61
75
 
@@ -99,7 +113,7 @@ def login(
99
113
  api_key: str | None,
100
114
  device: bool,
101
115
  ) -> None:
102
- """Login to PyResolve to enable cloud features.
116
+ """Login to Codeshift to enable cloud features.
103
117
 
104
118
  \b
105
119
  Authentication methods:
@@ -107,7 +121,8 @@ def login(
107
121
  2. API key: codeshift login -k pyr_xxxxx
108
122
  3. Device flow: codeshift login --device
109
123
 
110
- Your credentials are stored in ~/.config/codeshift/credentials.json
124
+ Your credentials are stored securely in ~/.config/codeshift/credentials.enc
125
+ using AES encryption.
111
126
 
112
127
  Don't have an account? Run: codeshift register
113
128
  """
@@ -148,13 +163,14 @@ def register(
148
163
  password: str | None,
149
164
  name: str | None,
150
165
  ) -> None:
151
- """Create a new PyResolve account.
166
+ """Create a new Codeshift account.
152
167
 
153
168
  \b
154
169
  Example:
155
170
  codeshift register -e user@example.com -p yourpassword
156
171
 
157
- Your credentials are stored in ~/.config/codeshift/credentials.json
172
+ Your credentials are stored securely in ~/.config/codeshift/credentials.enc
173
+ using AES encryption.
158
174
  """
159
175
  # Check if already logged in
160
176
  existing = load_credentials()
@@ -211,7 +227,7 @@ def _register_account(email: str, password: str, full_name: str | None) -> None:
211
227
  if response.status_code == 200:
212
228
  data = response.json()
213
229
 
214
- # Save credentials
230
+ # Save credentials securely
215
231
  save_credentials(
216
232
  {
217
233
  "api_key": data["api_key"],
@@ -228,7 +244,7 @@ def _register_account(email: str, password: str, full_name: str | None) -> None:
228
244
  f"[green]Account created successfully![/]\n\n"
229
245
  f"Email: [cyan]{data['user']['email']}[/]\n"
230
246
  f"Tier: [cyan]{data['user'].get('tier', 'free')}[/]\n\n"
231
- f"[dim]You are now logged in and ready to use PyResolve.[/]",
247
+ f"[dim]You are now logged in and ready to use Codeshift.[/]",
232
248
  title="Registration Successful",
233
249
  )
234
250
  )
@@ -275,7 +291,7 @@ def _login_with_api_key(api_key: str) -> None:
275
291
  if response.status_code == 200:
276
292
  user = response.json()
277
293
 
278
- # Save credentials
294
+ # Save credentials securely
279
295
  save_credentials(
280
296
  {
281
297
  "api_key": api_key,
@@ -327,7 +343,7 @@ def _login_with_password(email: str, password: str) -> None:
327
343
  if response.status_code == 200:
328
344
  data = response.json()
329
345
 
330
- # Save credentials
346
+ # Save credentials securely
331
347
  save_credentials(
332
348
  {
333
349
  "api_key": data["api_key"],
@@ -422,7 +438,7 @@ def _login_with_device_code() -> None:
422
438
  if response.status_code == 200:
423
439
  data = response.json()
424
440
 
425
- # Save credentials
441
+ # Save credentials securely
426
442
  save_credentials(
427
443
  {
428
444
  "api_key": data["api_key"],
@@ -466,7 +482,7 @@ def _login_with_device_code() -> None:
466
482
 
467
483
  @click.command()
468
484
  def logout() -> None:
469
- """Logout from PyResolve and remove saved credentials."""
485
+ """Logout from Codeshift and remove saved credentials."""
470
486
  creds = load_credentials()
471
487
 
472
488
  if not creds:
@@ -487,7 +503,7 @@ def logout() -> None:
487
503
  except httpx.RequestError:
488
504
  pass
489
505
 
490
- # Delete local credentials
506
+ # Delete local credentials securely
491
507
  delete_credentials()
492
508
 
493
509
  console.print("[green]Successfully logged out[/]")
@@ -11,10 +11,7 @@ from rich.panel import Panel
11
11
  from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
12
12
  from rich.table import Table
13
13
 
14
- from codeshift.knowledge import (
15
- generate_knowledge_base_sync,
16
- is_tier_1_library,
17
- )
14
+ from codeshift.knowledge import generate_knowledge_base_sync, is_tier_1_library
18
15
  from codeshift.scanner import DependencyParser
19
16
  from codeshift.utils.config import ProjectConfig
20
17
 
@@ -148,7 +145,7 @@ def scan(
148
145
  console.print(
149
146
  Panel(
150
147
  "[bold]Scanning project for possible migrations[/]\n\n" f"Path: {project_path}",
151
- title="PyResolve Scan",
148
+ title="Codeshift Scan",
152
149
  )
153
150
  )
154
151
 
@@ -10,12 +10,7 @@ from rich.panel import Panel
10
10
  from rich.progress import Progress, SpinnerColumn, TextColumn
11
11
  from rich.table import Table
12
12
 
13
- from codeshift.cli.quota import (
14
- QuotaError,
15
- check_quota,
16
- record_usage,
17
- show_quota_exceeded_message,
18
- )
13
+ from codeshift.cli.quota import QuotaError, check_quota, record_usage, show_quota_exceeded_message
19
14
  from codeshift.knowledge import (
20
15
  Confidence,
21
16
  GeneratedKnowledgeBase,
@@ -23,12 +18,8 @@ from codeshift.knowledge import (
23
18
  is_tier_1_library,
24
19
  )
25
20
  from codeshift.knowledge_base import KnowledgeBaseLoader
26
- from codeshift.migrator.ast_transforms import TransformChange, TransformResult, TransformStatus
27
- from codeshift.migrator.transforms.fastapi_transformer import transform_fastapi
28
- from codeshift.migrator.transforms.pandas_transformer import transform_pandas
29
- from codeshift.migrator.transforms.pydantic_v1_to_v2 import transform_pydantic_v1_to_v2
30
- from codeshift.migrator.transforms.requests_transformer import transform_requests
31
- from codeshift.migrator.transforms.sqlalchemy_transformer import transform_sqlalchemy
21
+ from codeshift.knowledge_base.models import LibraryKnowledge
22
+ from codeshift.migrator.ast_transforms import TransformResult, TransformStatus
32
23
  from codeshift.scanner import CodeScanner, DependencyParser
33
24
  from codeshift.utils.config import ProjectConfig
34
25
 
@@ -86,6 +77,11 @@ def save_state(project_path: Path, state: dict) -> None:
86
77
  is_flag=True,
87
78
  help="Show detailed output",
88
79
  )
80
+ @click.option(
81
+ "--force-llm",
82
+ is_flag=True,
83
+ help="Force LLM migration even for libraries with AST transforms",
84
+ )
89
85
  def upgrade(
90
86
  library: str,
91
87
  target: str,
@@ -93,6 +89,7 @@ def upgrade(
93
89
  file: str | None,
94
90
  dry_run: bool,
95
91
  verbose: bool,
92
+ force_llm: bool,
96
93
  ) -> None:
97
94
  """Analyze your codebase and propose changes for a library upgrade.
98
95
 
@@ -105,34 +102,44 @@ def upgrade(
105
102
  project_path = Path(path).resolve()
106
103
  project_config = ProjectConfig.from_pyproject(project_path)
107
104
 
108
- # Check quota before starting (allow offline for Tier 1 libraries)
105
+ # Check quota before starting (allow offline for Tier 1 libraries unless force-llm)
109
106
  is_tier1 = is_tier_1_library(library)
110
107
  try:
111
- check_quota("file_migrated", quantity=1, allow_offline=is_tier1)
108
+ check_quota("file_migrated", quantity=1, allow_offline=is_tier1 and not force_llm)
112
109
  except QuotaError as e:
113
110
  show_quota_exceeded_message(e)
114
111
  raise SystemExit(1) from e
115
112
 
116
- # Load knowledge base
113
+ # Load knowledge base (optional - YAML may not exist for all libraries)
117
114
  loader = KnowledgeBaseLoader()
115
+ knowledge: LibraryKnowledge | None = None
118
116
 
119
117
  try:
120
118
  knowledge = loader.load(library)
121
- except FileNotFoundError as e:
122
- console.print(f"[red]Error:[/] {e}")
123
- console.print(f"\nSupported libraries: {', '.join(loader.get_supported_libraries())}")
124
- raise SystemExit(1) from e
119
+ except FileNotFoundError:
120
+ if verbose:
121
+ console.print(
122
+ f"[dim]No knowledge base YAML for {library} - using generated knowledge[/]"
123
+ )
125
124
 
126
- # Check if migration is supported
127
- # For now, we'll allow any version since we're doing a general migration
128
- console.print(
129
- Panel(
130
- f"[bold]Upgrading {knowledge.display_name}[/] to version [cyan]{target}[/]\n\n"
131
- f"{knowledge.description}\n"
132
- f"Migration guide: {knowledge.migration_guide_url or 'N/A'}",
133
- title="PyResolve Migration",
125
+ # Display migration info with fallback for missing YAML
126
+ if knowledge:
127
+ console.print(
128
+ Panel(
129
+ f"[bold]Upgrading {knowledge.display_name}[/] to version [cyan]{target}[/]\n\n"
130
+ f"{knowledge.description}\n"
131
+ f"Migration guide: {knowledge.migration_guide_url or 'N/A'}",
132
+ title="Codeshift Migration",
133
+ )
134
+ )
135
+ else:
136
+ console.print(
137
+ Panel(
138
+ f"[bold]Upgrading {library}[/] to version [cyan]{target}[/]\n\n"
139
+ "Using AI-powered migration (no static knowledge base available)",
140
+ title="Codeshift Migration",
141
+ )
134
142
  )
135
- )
136
143
 
137
144
  # Step 1: Parse dependencies
138
145
  with Progress(
@@ -276,7 +283,25 @@ def upgrade(
276
283
  console.print(f"\n[yellow]No {library} imports found in the codebase.[/]")
277
284
  return
278
285
 
279
- # Step 4: Apply transforms
286
+ # Step 4: Apply transforms using MigrationEngine
287
+ # Import here to avoid circular dependency (upgrade.py -> migrator -> llm_migrator -> api_client -> auth -> cli -> upgrade.py)
288
+ from codeshift.migrator import get_migration_engine
289
+
290
+ engine = get_migration_engine()
291
+
292
+ # Check auth for non-Tier1 libraries or force-llm mode
293
+ llm_required = force_llm or not is_tier1
294
+ if llm_required and not engine.llm_migrator.is_available:
295
+ console.print(
296
+ Panel(
297
+ f"[yellow]LLM migration required for {library}[/]\n\n"
298
+ "Run [cyan]codeshift login[/] and upgrade to Pro tier for LLM features.",
299
+ title="Authentication Required",
300
+ )
301
+ )
302
+ if not is_tier1:
303
+ raise SystemExit(1)
304
+
280
305
  with Progress(
281
306
  SpinnerColumn(),
282
307
  TextColumn("[progress.description]{task.description}"),
@@ -291,45 +316,28 @@ def upgrade(
291
316
 
292
317
  results: list[TransformResult] = []
293
318
 
319
+ def migration_progress(msg: str) -> None:
320
+ progress.update(task, description=msg)
321
+
294
322
  for file_path in files_to_transform:
295
323
  try:
296
324
  source_code = file_path.read_text()
297
325
 
298
- # Select transformer based on library
299
- transform_func = {
300
- "pydantic": transform_pydantic_v1_to_v2,
301
- "fastapi": transform_fastapi,
302
- "sqlalchemy": transform_sqlalchemy,
303
- "pandas": transform_pandas,
304
- "requests": transform_requests,
305
- }.get(library)
306
-
307
- if transform_func:
308
- transformed_code, changes = transform_func(source_code)
309
- # Create TransformResult from the function output
310
- result = TransformResult(
311
- file_path=file_path,
312
- status=TransformStatus.SUCCESS if changes else TransformStatus.NO_CHANGES,
313
- original_code=source_code,
314
- transformed_code=transformed_code,
315
- changes=[
316
- TransformChange(
317
- description=c.description,
318
- line_number=c.line_number,
319
- original=c.original,
320
- replacement=c.replacement,
321
- transform_name=c.transform_name,
322
- confidence=getattr(c, "confidence", 1.0),
323
- )
324
- for c in changes
325
- ],
326
- )
327
- else:
328
- console.print(f"[yellow]Warning:[/] No transformer available for {library}")
329
- continue
326
+ result = engine.run_migration(
327
+ code=source_code,
328
+ file_path=file_path,
329
+ library=library,
330
+ old_version=current_version or "1.0",
331
+ new_version=target,
332
+ knowledge_base=generated_kb,
333
+ progress_callback=migration_progress if verbose else None,
334
+ )
330
335
 
331
336
  if result.has_changes:
332
337
  results.append(result)
338
+ elif result.errors:
339
+ for error in result.errors:
340
+ console.print(f"[yellow]Warning ({file_path.name}):[/] {error}")
333
341
 
334
342
  except Exception as e:
335
343
  console.print(f"[red]Error processing {file_path}:[/] {e}")
@@ -234,7 +234,7 @@ def upgrade_all(
234
234
  console.print(
235
235
  Panel(
236
236
  "[bold]Scanning project for upgradeable dependencies[/]\n\n" f"Path: {project_path}",
237
- title="PyResolve Upgrade All",
237
+ title="Codeshift Upgrade All",
238
238
  )
239
239
  )
240
240
 
codeshift/cli/main.py CHANGED
@@ -1,4 +1,4 @@
1
- """Main CLI entry point for PyResolve."""
1
+ """Main CLI entry point for Codeshift."""
2
2
 
3
3
  import click
4
4
  from rich.console import Console
@@ -26,7 +26,7 @@ console = Console()
26
26
  @click.version_option(version=__version__, prog_name="codeshift")
27
27
  @click.pass_context
28
28
  def cli(ctx: click.Context) -> None:
29
- """PyResolve - AI-powered Python dependency migration tool.
29
+ """Codeshift - AI-powered Python dependency migration tool.
30
30
 
31
31
  Don't just flag the update. Fix the break.
32
32
 
@@ -172,6 +172,12 @@ TIER_1_LIBRARIES = {
172
172
  "pytest",
173
173
  "marshmallow",
174
174
  "flask",
175
+ "celery",
176
+ "httpx",
177
+ "aiohttp",
178
+ "click",
179
+ "attrs",
180
+ "django",
175
181
  }
176
182
 
177
183
 
@@ -69,7 +69,7 @@ breaking_changes:
69
69
 
70
70
  # connector_owner default change
71
71
  - symbol: "ClientSession(connector=..., connector_owner=None)"
72
- change_type: behavior_change
72
+ change_type: behavior_changed
73
73
  severity: medium
74
74
  from_version: "3.7"
75
75
  to_version: "3.9"
@@ -133,7 +133,7 @@ breaking_changes:
133
133
 
134
134
  # Middleware signature changes (old-style to new-style)
135
135
  - symbol: "@middleware"
136
- change_type: signature_change
136
+ change_type: signature_changed
137
137
  severity: high
138
138
  from_version: "3.7"
139
139
  to_version: "3.9"
@@ -154,7 +154,7 @@ breaking_changes:
154
154
  transform_name: ws_connect_timeout_rename
155
155
 
156
156
  - symbol: "ws_connect(receive_timeout=...)"
157
- change_type: behavior_change
157
+ change_type: behavior_changed
158
158
  severity: low
159
159
  from_version: "3.7"
160
160
  to_version: "3.9"
@@ -119,7 +119,7 @@ breaking_changes:
119
119
 
120
120
  # Response.iter_lines() behavior change
121
121
  - symbol: "Response.iter_lines()"
122
- change_type: behavior_change
122
+ change_type: behavior_changed
123
123
  severity: medium
124
124
  from_version: "0.23"
125
125
  to_version: "0.24"
@@ -130,7 +130,7 @@ breaking_changes:
130
130
 
131
131
  # NetRC authentication change
132
132
  - symbol: "trust_env=True"
133
- change_type: behavior_change
133
+ change_type: behavior_changed
134
134
  severity: medium
135
135
  from_version: "0.23"
136
136
  to_version: "0.24"
@@ -141,7 +141,7 @@ breaking_changes:
141
141
 
142
142
  # Query parameter encoding change
143
143
  - symbol: "params encoding"
144
- change_type: behavior_change
144
+ change_type: behavior_changed
145
145
  severity: low
146
146
  from_version: "0.23"
147
147
  to_version: "0.24"
@@ -173,7 +173,7 @@ breaking_changes:
173
173
 
174
174
  # Follow redirects default change
175
175
  - symbol: "follow_redirects"
176
- change_type: behavior_change
176
+ change_type: behavior_changed
177
177
  severity: high
178
178
  from_version: "0.19"
179
179
  to_version: "0.20"
@@ -182,7 +182,7 @@ breaking_changes:
182
182
 
183
183
  # pytest.importorskip exc_type
184
184
  - symbol: "pytest.importorskip()"
185
- change_type: behavior_change
185
+ change_type: behavior_changed
186
186
  severity: low
187
187
  from_version: "8.0"
188
188
  to_version: "8.2"
@@ -9,6 +9,7 @@ class ChangeType(Enum):
9
9
 
10
10
  RENAMED = "renamed"
11
11
  REMOVED = "removed"
12
+ MOVED = "moved"
12
13
  SIGNATURE_CHANGED = "signature_changed"
13
14
  BEHAVIOR_CHANGED = "behavior_changed"
14
15
  DEPRECATED = "deprecated"
@@ -1,6 +1,6 @@
1
1
  """LLM-based migration for complex cases.
2
2
 
3
- LLM migrations (Tier 2/3) are routed through the PyResolve API,
3
+ LLM migrations (Tier 2/3) are routed through the Codeshift API,
4
4
  which handles authentication, quota checking, and billing.
5
5
  Users must have a Pro or Unlimited subscription to use LLM features.
6
6
 
@@ -11,12 +11,8 @@ import re
11
11
  from dataclasses import dataclass
12
12
  from pathlib import Path
13
13
 
14
- from codeshift.migrator.ast_transforms import (
15
- TransformChange,
16
- TransformResult,
17
- TransformStatus,
18
- )
19
- from codeshift.utils.api_client import PyResolveAPIClient, get_api_client
14
+ from codeshift.migrator.ast_transforms import TransformChange, TransformResult, TransformStatus
15
+ from codeshift.utils.api_client import CodeshiftAPIClient, get_api_client
20
16
  from codeshift.utils.cache import LLMCache, get_llm_cache
21
17
  from codeshift.validator.syntax_checker import quick_syntax_check
22
18
 
@@ -35,10 +31,10 @@ class LLMMigrationResult:
35
31
 
36
32
 
37
33
  class LLMMigrator:
38
- """Handles complex migrations using LLM via the PyResolve API.
34
+ """Handles complex migrations using LLM via the Codeshift API.
39
35
 
40
36
  LLM migrations require authentication and a Pro or Unlimited subscription.
41
- All LLM calls are routed through the PyResolve API, which handles:
37
+ All LLM calls are routed through the Codeshift API, which handles:
42
38
  - Authentication and authorization
43
39
  - Quota checking and billing
44
40
  - Server-side Anthropic API calls
@@ -49,7 +45,7 @@ class LLMMigrator:
49
45
 
50
46
  def __init__(
51
47
  self,
52
- client: PyResolveAPIClient | None = None,
48
+ client: CodeshiftAPIClient | None = None,
53
49
  cache: LLMCache | None = None,
54
50
  use_cache: bool = True,
55
51
  validate_output: bool = True,
@@ -80,7 +76,7 @@ class LLMMigrator:
80
76
  to_version: str,
81
77
  context: str | None = None,
82
78
  ) -> LLMMigrationResult:
83
- """Migrate code using the LLM via PyResolve API.
79
+ """Migrate code using the LLM via Codeshift API.
84
80
 
85
81
  Args:
86
82
  code: Source code to migrate
@@ -114,7 +110,7 @@ class LLMMigrator:
114
110
  used_cache=True,
115
111
  )
116
112
 
117
- # Call PyResolve API (which calls Anthropic server-side)
113
+ # Call Codeshift API (which calls Anthropic server-side)
118
114
  response = self.client.migrate_code(
119
115
  code=code,
120
116
  library=library,