repr-cli 0.2.7__tar.gz → 0.2.8__tar.gz

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 (39) hide show
  1. {repr_cli-0.2.7/repr_cli.egg-info → repr_cli-0.2.8}/PKG-INFO +1 -1
  2. {repr_cli-0.2.7 → repr_cli-0.2.8}/pyproject.toml +1 -1
  3. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/__init__.py +1 -1
  4. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/cli.py +28 -0
  5. repr_cli-0.2.8/repr/updater.py +245 -0
  6. {repr_cli-0.2.7 → repr_cli-0.2.8/repr_cli.egg-info}/PKG-INFO +1 -1
  7. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr_cli.egg-info/SOURCES.txt +1 -0
  8. {repr_cli-0.2.7 → repr_cli-0.2.8}/LICENSE +0 -0
  9. {repr_cli-0.2.7 → repr_cli-0.2.8}/README.md +0 -0
  10. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/__main__.py +0 -0
  11. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/api.py +0 -0
  12. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/auth.py +0 -0
  13. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/config.py +0 -0
  14. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/discovery.py +0 -0
  15. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/doctor.py +0 -0
  16. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/extractor.py +0 -0
  17. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/hooks.py +0 -0
  18. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/keychain.py +0 -0
  19. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/llm.py +0 -0
  20. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/openai_analysis.py +0 -0
  21. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/privacy.py +0 -0
  22. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/storage.py +0 -0
  23. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/telemetry.py +0 -0
  24. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/templates.py +0 -0
  25. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/tools.py +0 -0
  26. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr/ui.py +0 -0
  27. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr_cli.egg-info/dependency_links.txt +0 -0
  28. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr_cli.egg-info/entry_points.txt +0 -0
  29. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr_cli.egg-info/requires.txt +0 -0
  30. {repr_cli-0.2.7 → repr_cli-0.2.8}/repr_cli.egg-info/top_level.txt +0 -0
  31. {repr_cli-0.2.7 → repr_cli-0.2.8}/setup.cfg +0 -0
  32. {repr_cli-0.2.7 → repr_cli-0.2.8}/setup.py +0 -0
  33. {repr_cli-0.2.7 → repr_cli-0.2.8}/tests/test_environment_variables.py +0 -0
  34. {repr_cli-0.2.7 → repr_cli-0.2.8}/tests/test_network_sandboxing.py +0 -0
  35. {repr_cli-0.2.7 → repr_cli-0.2.8}/tests/test_privacy_guarantees.py +0 -0
  36. {repr_cli-0.2.7 → repr_cli-0.2.8}/tests/test_profile_export.py +0 -0
  37. {repr_cli-0.2.7 → repr_cli-0.2.8}/tests/test_repo_identity.py +0 -0
  38. {repr_cli-0.2.7 → repr_cli-0.2.8}/tests/test_stories_review.py +0 -0
  39. {repr_cli-0.2.7 → repr_cli-0.2.8}/tests/test_token_budget.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repr-cli
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: A beautiful, privacy-first CLI that analyzes your code repositories and generates a compelling developer profile
5
5
  Author-email: Repr <hello@repr.dev>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "repr-cli"
7
- version = "0.2.7"
7
+ version = "0.2.8"
8
8
  description = "A beautiful, privacy-first CLI that analyzes your code repositories and generates a compelling developer profile"
9
9
  readme = "README.md"
10
10
  license = {file = "LICENSE"}
@@ -10,6 +10,6 @@ try:
10
10
  __version__ = version("repr-cli")
11
11
  except Exception:
12
12
  # Fallback for PyInstaller builds where metadata isn't available
13
- __version__ = "0.2.7"
13
+ __version__ = "0.2.8"
14
14
  __author__ = "Repr"
15
15
  __email__ = "hello@repr.dev"
@@ -2382,6 +2382,34 @@ def mode():
2382
2382
  console.print(f" [{BRAND_MUTED}]✗[/] Publishing ({reason})")
2383
2383
 
2384
2384
 
2385
+ @app.command()
2386
+ def update(
2387
+ check: bool = typer.Option(False, "--check", "-c", help="Only check for updates, don't install"),
2388
+ force: bool = typer.Option(False, "--force", "-f", help="Force update even if already up to date"),
2389
+ ):
2390
+ """
2391
+ Update repr to the latest version.
2392
+
2393
+ Automatically detects installation method (Homebrew, pip, or binary)
2394
+ and updates accordingly.
2395
+
2396
+ Examples:
2397
+ repr update # Update to latest version
2398
+ repr update --check # Just check if update available
2399
+ """
2400
+ from .updater import check_for_update, perform_update
2401
+
2402
+ if check:
2403
+ new_version = check_for_update()
2404
+ if new_version:
2405
+ print_info(f"New version available: v{new_version}")
2406
+ print_info("Run 'repr update' to install")
2407
+ else:
2408
+ print_success(f"Already up to date (v{__version__})")
2409
+ else:
2410
+ perform_update(force=force)
2411
+
2412
+
2385
2413
  @app.command()
2386
2414
  def doctor():
2387
2415
  """
@@ -0,0 +1,245 @@
1
+ """
2
+ Auto-update functionality for repr CLI.
3
+ """
4
+
5
+ import os
6
+ import platform
7
+ import shutil
8
+ import subprocess
9
+ import sys
10
+ import tarfile
11
+ import tempfile
12
+ from pathlib import Path
13
+ from typing import Optional, Tuple
14
+
15
+ import httpx
16
+
17
+ from . import __version__
18
+ from .ui import console, print_success, print_error, print_warning, print_info
19
+
20
+ GITHUB_REPO = "repr-app/cli"
21
+ GITHUB_API_URL = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest"
22
+
23
+
24
+ def parse_version(version: str) -> Tuple[int, ...]:
25
+ """Parse version string into tuple for comparison."""
26
+ # Remove 'v' prefix if present
27
+ version = version.lstrip('v')
28
+ try:
29
+ return tuple(int(x) for x in version.split('.'))
30
+ except ValueError:
31
+ return (0, 0, 0)
32
+
33
+
34
+ def get_latest_version() -> Optional[dict]:
35
+ """Fetch latest release info from GitHub."""
36
+ try:
37
+ with httpx.Client(timeout=10) as client:
38
+ response = client.get(GITHUB_API_URL)
39
+ response.raise_for_status()
40
+ return response.json()
41
+ except Exception as e:
42
+ print_error(f"Failed to check for updates: {e}")
43
+ return None
44
+
45
+
46
+ def check_for_update() -> Optional[str]:
47
+ """
48
+ Check if a newer version is available.
49
+ Returns the new version string if available, None otherwise.
50
+ """
51
+ release = get_latest_version()
52
+ if not release:
53
+ return None
54
+
55
+ latest_version = release.get("tag_name", "").lstrip('v')
56
+ current = parse_version(__version__)
57
+ latest = parse_version(latest_version)
58
+
59
+ if latest > current:
60
+ return latest_version
61
+ return None
62
+
63
+
64
+ def get_installation_method() -> str:
65
+ """Detect how repr was installed."""
66
+ executable = sys.executable
67
+ repr_path = shutil.which("repr")
68
+
69
+ if repr_path:
70
+ # Check if installed via Homebrew
71
+ if "/homebrew/" in repr_path.lower() or "/cellar/" in repr_path.lower():
72
+ return "homebrew"
73
+
74
+ # Check if it's a standalone binary (PyInstaller)
75
+ if getattr(sys, 'frozen', False):
76
+ return "binary"
77
+
78
+ # Check if installed via pip
79
+ try:
80
+ result = subprocess.run(
81
+ [sys.executable, "-m", "pip", "show", "repr-cli"],
82
+ capture_output=True,
83
+ text=True
84
+ )
85
+ if result.returncode == 0:
86
+ return "pip"
87
+ except Exception:
88
+ pass
89
+
90
+ return "unknown"
91
+
92
+
93
+ def update_via_homebrew() -> bool:
94
+ """Update using Homebrew."""
95
+ print_info("Updating via Homebrew...")
96
+ try:
97
+ subprocess.run(["brew", "update"], check=True)
98
+ subprocess.run(["brew", "upgrade", "repr"], check=True)
99
+ return True
100
+ except subprocess.CalledProcessError as e:
101
+ print_error(f"Homebrew update failed: {e}")
102
+ return False
103
+
104
+
105
+ def update_via_pip() -> bool:
106
+ """Update using pip."""
107
+ print_info("Updating via pip...")
108
+ try:
109
+ subprocess.run(
110
+ [sys.executable, "-m", "pip", "install", "--upgrade", "repr-cli"],
111
+ check=True
112
+ )
113
+ return True
114
+ except subprocess.CalledProcessError as e:
115
+ print_error(f"pip update failed: {e}")
116
+ return False
117
+
118
+
119
+ def update_via_binary(release: dict) -> bool:
120
+ """Update standalone binary by downloading from GitHub releases."""
121
+ system = platform.system().lower()
122
+
123
+ # Determine asset name
124
+ if system == "darwin":
125
+ asset_name = "repr-macos.tar.gz"
126
+ elif system == "linux":
127
+ asset_name = "repr-linux.tar.gz"
128
+ elif system == "windows":
129
+ asset_name = "repr-windows.exe"
130
+ else:
131
+ print_error(f"Unsupported platform: {system}")
132
+ return False
133
+
134
+ # Find the asset URL
135
+ asset_url = None
136
+ for asset in release.get("assets", []):
137
+ if asset["name"] == asset_name:
138
+ asset_url = asset["browser_download_url"]
139
+ break
140
+
141
+ if not asset_url:
142
+ print_error(f"Could not find {asset_name} in release assets")
143
+ return False
144
+
145
+ print_info(f"Downloading {asset_name}...")
146
+
147
+ try:
148
+ # Get current executable path
149
+ current_exe = shutil.which("repr") or sys.argv[0]
150
+ current_exe = Path(current_exe).resolve()
151
+
152
+ with tempfile.TemporaryDirectory() as tmpdir:
153
+ tmpdir = Path(tmpdir)
154
+
155
+ # Download the asset
156
+ with httpx.Client(timeout=60, follow_redirects=True) as client:
157
+ response = client.get(asset_url)
158
+ response.raise_for_status()
159
+
160
+ download_path = tmpdir / asset_name
161
+ download_path.write_bytes(response.content)
162
+
163
+ # Extract if it's a tarball
164
+ if asset_name.endswith(".tar.gz"):
165
+ with tarfile.open(download_path, "r:gz") as tar:
166
+ tar.extractall(tmpdir)
167
+ new_exe = tmpdir / "repr"
168
+ else:
169
+ new_exe = download_path
170
+
171
+ # Make executable
172
+ new_exe.chmod(0o755)
173
+
174
+ # Replace current executable
175
+ backup_path = current_exe.with_suffix(".old")
176
+
177
+ # Backup current
178
+ if current_exe.exists():
179
+ shutil.move(str(current_exe), str(backup_path))
180
+
181
+ # Install new
182
+ try:
183
+ shutil.move(str(new_exe), str(current_exe))
184
+ # Remove backup on success
185
+ if backup_path.exists():
186
+ backup_path.unlink()
187
+ except Exception:
188
+ # Restore backup on failure
189
+ if backup_path.exists():
190
+ shutil.move(str(backup_path), str(current_exe))
191
+ raise
192
+
193
+ return True
194
+
195
+ except Exception as e:
196
+ print_error(f"Binary update failed: {e}")
197
+ return False
198
+
199
+
200
+ def perform_update(force: bool = False) -> bool:
201
+ """
202
+ Check for and perform update.
203
+ Returns True if update was successful or not needed.
204
+ """
205
+ print_info(f"Current version: {__version__}")
206
+
207
+ release = get_latest_version()
208
+ if not release:
209
+ return False
210
+
211
+ latest_version = release.get("tag_name", "").lstrip('v')
212
+ current = parse_version(__version__)
213
+ latest = parse_version(latest_version)
214
+
215
+ if latest <= current and not force:
216
+ print_success(f"Already up to date (v{__version__})")
217
+ return True
218
+
219
+ print_info(f"New version available: v{latest_version}")
220
+
221
+ method = get_installation_method()
222
+ print_info(f"Installation method: {method}")
223
+
224
+ success = False
225
+
226
+ if method == "homebrew":
227
+ success = update_via_homebrew()
228
+ elif method == "pip":
229
+ success = update_via_pip()
230
+ elif method == "binary":
231
+ success = update_via_binary(release)
232
+ else:
233
+ print_warning("Could not detect installation method.")
234
+ print_info("Try one of these manually:")
235
+ console.print(" • brew upgrade repr")
236
+ console.print(" • pip install --upgrade repr-cli")
237
+ console.print(f" • Download from https://github.com/{GITHUB_REPO}/releases/latest")
238
+ return False
239
+
240
+ if success:
241
+ print_success(f"Updated to v{latest_version}")
242
+ print_info("Restart your terminal or run 'repr -v' to verify")
243
+
244
+ return success
245
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repr-cli
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: A beautiful, privacy-first CLI that analyzes your code repositories and generates a compelling developer profile
5
5
  Author-email: Repr <hello@repr.dev>
6
6
  License: MIT License
@@ -21,6 +21,7 @@ repr/telemetry.py
21
21
  repr/templates.py
22
22
  repr/tools.py
23
23
  repr/ui.py
24
+ repr/updater.py
24
25
  repr_cli.egg-info/PKG-INFO
25
26
  repr_cli.egg-info/SOURCES.txt
26
27
  repr_cli.egg-info/dependency_links.txt
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes