repr-cli 0.2.4__py3-none-any.whl → 0.2.6__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.
- repr/__init__.py +6 -1
- repr/cli.py +90 -0
- repr/telemetry.py +279 -0
- repr_cli-0.2.6.dist-info/METADATA +370 -0
- {repr_cli-0.2.4.dist-info → repr_cli-0.2.6.dist-info}/RECORD +9 -8
- repr_cli-0.2.4.dist-info/METADATA +0 -283
- {repr_cli-0.2.4.dist-info → repr_cli-0.2.6.dist-info}/WHEEL +0 -0
- {repr_cli-0.2.4.dist-info → repr_cli-0.2.6.dist-info}/entry_points.txt +0 -0
- {repr_cli-0.2.4.dist-info → repr_cli-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {repr_cli-0.2.4.dist-info → repr_cli-0.2.6.dist-info}/top_level.txt +0 -0
repr/__init__.py
CHANGED
|
@@ -5,6 +5,11 @@ Analyzes your local git repositories and generates a compelling
|
|
|
5
5
|
developer profile without ever sending your source code to the cloud.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
try:
|
|
9
|
+
from importlib.metadata import version
|
|
10
|
+
__version__ = version("repr-cli")
|
|
11
|
+
except Exception:
|
|
12
|
+
# Fallback for PyInstaller builds where metadata isn't available
|
|
13
|
+
__version__ = "0.2.6"
|
|
9
14
|
__author__ = "Repr"
|
|
10
15
|
__email__ = "hello@repr.dev"
|
repr/cli.py
CHANGED
|
@@ -136,6 +136,7 @@ def dev_callback(value: bool):
|
|
|
136
136
|
|
|
137
137
|
@app.callback()
|
|
138
138
|
def main(
|
|
139
|
+
ctx: typer.Context,
|
|
139
140
|
version: bool = typer.Option(
|
|
140
141
|
False, "--version", "-v",
|
|
141
142
|
callback=version_callback,
|
|
@@ -155,6 +156,11 @@ def main(
|
|
|
155
156
|
"""
|
|
156
157
|
# Migrate plaintext auth tokens on startup
|
|
157
158
|
migrate_plaintext_auth()
|
|
159
|
+
|
|
160
|
+
# Track command usage (if telemetry enabled)
|
|
161
|
+
from .telemetry import track_command
|
|
162
|
+
if ctx.invoked_subcommand:
|
|
163
|
+
track_command(ctx.invoked_subcommand)
|
|
158
164
|
|
|
159
165
|
|
|
160
166
|
# =============================================================================
|
|
@@ -1686,6 +1692,90 @@ def llm_test():
|
|
|
1686
1692
|
# PRIVACY
|
|
1687
1693
|
# =============================================================================
|
|
1688
1694
|
|
|
1695
|
+
@privacy_app.command("telemetry")
|
|
1696
|
+
def privacy_telemetry(
|
|
1697
|
+
action: str = typer.Argument("status", help="Action: status, enable, disable"),
|
|
1698
|
+
json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
1699
|
+
):
|
|
1700
|
+
"""
|
|
1701
|
+
Manage anonymous telemetry.
|
|
1702
|
+
|
|
1703
|
+
Telemetry is disabled by default. When enabled, it sends:
|
|
1704
|
+
- Command names and timestamps
|
|
1705
|
+
- OS type and CLI version
|
|
1706
|
+
- Anonymous device ID (random, not linked to you)
|
|
1707
|
+
|
|
1708
|
+
It NEVER sends: code, commits, paths, usernames, emails.
|
|
1709
|
+
|
|
1710
|
+
Examples:
|
|
1711
|
+
repr privacy telemetry Show telemetry status
|
|
1712
|
+
repr privacy telemetry enable Opt-in to telemetry
|
|
1713
|
+
repr privacy telemetry disable Opt-out of telemetry
|
|
1714
|
+
"""
|
|
1715
|
+
from .telemetry import (
|
|
1716
|
+
is_telemetry_enabled,
|
|
1717
|
+
enable_telemetry,
|
|
1718
|
+
disable_telemetry,
|
|
1719
|
+
get_queue_stats,
|
|
1720
|
+
get_pending_events,
|
|
1721
|
+
)
|
|
1722
|
+
|
|
1723
|
+
if action == "status":
|
|
1724
|
+
stats = get_queue_stats()
|
|
1725
|
+
|
|
1726
|
+
if json_output:
|
|
1727
|
+
print(json.dumps(stats, indent=2))
|
|
1728
|
+
return
|
|
1729
|
+
|
|
1730
|
+
status = f"[{BRAND_SUCCESS}]enabled[/]" if stats["enabled"] else f"[{BRAND_MUTED}]disabled[/]"
|
|
1731
|
+
console.print(f"[bold]Telemetry[/]: {status}")
|
|
1732
|
+
console.print()
|
|
1733
|
+
|
|
1734
|
+
if stats["enabled"]:
|
|
1735
|
+
console.print(f"Device ID: {stats['device_id']}")
|
|
1736
|
+
console.print(f"Pending events: {stats['pending_events']}")
|
|
1737
|
+
console.print()
|
|
1738
|
+
|
|
1739
|
+
# Show pending events for transparency
|
|
1740
|
+
pending = get_pending_events()
|
|
1741
|
+
if pending:
|
|
1742
|
+
console.print("[bold]Pending events:[/]")
|
|
1743
|
+
for event in pending[-5:]:
|
|
1744
|
+
console.print(f" • {event.get('event', '')} ({event.get('timestamp', '')[:10]})")
|
|
1745
|
+
if len(pending) > 5:
|
|
1746
|
+
console.print(f" [{BRAND_MUTED}]... and {len(pending) - 5} more[/]")
|
|
1747
|
+
else:
|
|
1748
|
+
console.print("Enable with: repr privacy telemetry enable")
|
|
1749
|
+
|
|
1750
|
+
console.print()
|
|
1751
|
+
console.print(f"[{BRAND_MUTED}]Telemetry helps improve repr. No PII is ever sent.[/]")
|
|
1752
|
+
|
|
1753
|
+
elif action == "enable":
|
|
1754
|
+
enable_telemetry()
|
|
1755
|
+
print_success("Telemetry enabled")
|
|
1756
|
+
console.print()
|
|
1757
|
+
console.print("What we collect:")
|
|
1758
|
+
console.print(" ✓ Command names and timestamps")
|
|
1759
|
+
console.print(" ✓ OS type and CLI version")
|
|
1760
|
+
console.print(" ✓ Anonymous device ID")
|
|
1761
|
+
console.print()
|
|
1762
|
+
console.print("What we NEVER collect:")
|
|
1763
|
+
console.print(" ✗ Code, commits, diffs")
|
|
1764
|
+
console.print(" ✗ Paths, filenames")
|
|
1765
|
+
console.print(" ✗ Usernames, emails, PII")
|
|
1766
|
+
console.print()
|
|
1767
|
+
console.print(f"[{BRAND_MUTED}]Disable anytime: repr privacy telemetry disable[/]")
|
|
1768
|
+
|
|
1769
|
+
elif action == "disable":
|
|
1770
|
+
disable_telemetry()
|
|
1771
|
+
print_success("Telemetry disabled")
|
|
1772
|
+
console.print("No data will be collected.")
|
|
1773
|
+
|
|
1774
|
+
else:
|
|
1775
|
+
print_error(f"Unknown action: {action}")
|
|
1776
|
+
print_info("Valid actions: status, enable, disable")
|
|
1777
|
+
|
|
1778
|
+
|
|
1689
1779
|
@privacy_app.command("explain")
|
|
1690
1780
|
def privacy_explain():
|
|
1691
1781
|
"""
|
repr/telemetry.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Opt-in telemetry for repr CLI.
|
|
3
|
+
|
|
4
|
+
Privacy guarantees:
|
|
5
|
+
- Disabled by default (user must explicitly opt-in)
|
|
6
|
+
- Anonymous device ID (no PII)
|
|
7
|
+
- Events are logged locally first (visible via `repr privacy audit`)
|
|
8
|
+
- Only sends: command name, timestamp, OS, CLI version
|
|
9
|
+
- Never sends: code, commits, diffs, paths, usernames, emails
|
|
10
|
+
- Can be disabled at any time
|
|
11
|
+
|
|
12
|
+
Transparency:
|
|
13
|
+
- All sent events visible in `repr privacy audit`
|
|
14
|
+
- Source code is open (you're reading it!)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import platform
|
|
21
|
+
import uuid
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
import httpx
|
|
27
|
+
|
|
28
|
+
from . import __version__
|
|
29
|
+
from .config import (
|
|
30
|
+
CONFIG_DIR,
|
|
31
|
+
AUDIT_DIR,
|
|
32
|
+
load_config,
|
|
33
|
+
save_config,
|
|
34
|
+
get_config_value,
|
|
35
|
+
set_config_value,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Telemetry endpoint (can be overridden via env var for testing)
|
|
39
|
+
TELEMETRY_ENDPOINT = os.getenv(
|
|
40
|
+
"REPR_TELEMETRY_ENDPOINT",
|
|
41
|
+
"https://api.repr.dev/api/cli/telemetry"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Local telemetry queue file
|
|
45
|
+
TELEMETRY_QUEUE_FILE = AUDIT_DIR / "telemetry_queue.json"
|
|
46
|
+
|
|
47
|
+
# Device ID file (anonymous, persistent)
|
|
48
|
+
DEVICE_ID_FILE = CONFIG_DIR / ".device_id"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def is_telemetry_enabled() -> bool:
|
|
52
|
+
"""Check if telemetry is enabled."""
|
|
53
|
+
config = load_config()
|
|
54
|
+
return config.get("privacy", {}).get("telemetry_enabled", False)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def enable_telemetry() -> None:
|
|
58
|
+
"""Enable telemetry (opt-in)."""
|
|
59
|
+
set_config_value("privacy.telemetry_enabled", True)
|
|
60
|
+
|
|
61
|
+
# Track the opt-in event
|
|
62
|
+
track("telemetry_enabled", {"source": "cli"})
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def disable_telemetry() -> None:
|
|
66
|
+
"""Disable telemetry."""
|
|
67
|
+
set_config_value("privacy.telemetry_enabled", False)
|
|
68
|
+
|
|
69
|
+
# Clear any pending events
|
|
70
|
+
clear_queue()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_device_id() -> str:
|
|
74
|
+
"""
|
|
75
|
+
Get or create anonymous device ID.
|
|
76
|
+
|
|
77
|
+
The ID is:
|
|
78
|
+
- Random UUID, hashed for extra anonymity
|
|
79
|
+
- Persistent across sessions
|
|
80
|
+
- Not linked to any PII
|
|
81
|
+
"""
|
|
82
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
|
|
84
|
+
if DEVICE_ID_FILE.exists():
|
|
85
|
+
return DEVICE_ID_FILE.read_text().strip()
|
|
86
|
+
|
|
87
|
+
# Generate new anonymous ID
|
|
88
|
+
raw_id = str(uuid.uuid4())
|
|
89
|
+
# Hash it to ensure no UUID format that could be traced
|
|
90
|
+
device_id = hashlib.sha256(raw_id.encode()).hexdigest()[:32]
|
|
91
|
+
|
|
92
|
+
DEVICE_ID_FILE.write_text(device_id)
|
|
93
|
+
return device_id
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_context() -> dict[str, Any]:
|
|
97
|
+
"""
|
|
98
|
+
Get anonymous context for telemetry events.
|
|
99
|
+
|
|
100
|
+
Includes only:
|
|
101
|
+
- OS type and version
|
|
102
|
+
- CLI version
|
|
103
|
+
- Anonymous device ID
|
|
104
|
+
|
|
105
|
+
Never includes:
|
|
106
|
+
- Username, email, paths, code, etc.
|
|
107
|
+
"""
|
|
108
|
+
return {
|
|
109
|
+
"device_id": get_device_id(),
|
|
110
|
+
"cli_version": __version__,
|
|
111
|
+
"os": platform.system().lower(),
|
|
112
|
+
"os_version": platform.release(),
|
|
113
|
+
"python_version": platform.python_version(),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def track(event: str, properties: dict[str, Any] | None = None) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Track an event (if telemetry is enabled).
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
event: Event name (e.g., "command_run", "story_generated")
|
|
123
|
+
properties: Additional properties (must not contain PII)
|
|
124
|
+
"""
|
|
125
|
+
if not is_telemetry_enabled():
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
# Build event
|
|
129
|
+
event_data = {
|
|
130
|
+
"event": event,
|
|
131
|
+
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
132
|
+
"context": get_context(),
|
|
133
|
+
"properties": properties or {},
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Queue event locally
|
|
137
|
+
_queue_event(event_data)
|
|
138
|
+
|
|
139
|
+
# Try to flush queue (non-blocking)
|
|
140
|
+
_try_flush_queue()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def track_command(command: str, subcommand: str | None = None, success: bool = True) -> None:
|
|
144
|
+
"""
|
|
145
|
+
Track a CLI command execution.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
command: Main command name (e.g., "generate", "push")
|
|
149
|
+
subcommand: Optional subcommand (e.g., "llm add")
|
|
150
|
+
success: Whether command succeeded
|
|
151
|
+
"""
|
|
152
|
+
properties = {
|
|
153
|
+
"command": command,
|
|
154
|
+
"success": success,
|
|
155
|
+
}
|
|
156
|
+
if subcommand:
|
|
157
|
+
properties["subcommand"] = subcommand
|
|
158
|
+
|
|
159
|
+
track("command_run", properties)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def track_feature(feature: str, action: str, metadata: dict[str, Any] | None = None) -> None:
|
|
163
|
+
"""
|
|
164
|
+
Track feature usage.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
feature: Feature name (e.g., "local_llm", "cloud_sync")
|
|
168
|
+
action: Action taken (e.g., "configured", "used")
|
|
169
|
+
metadata: Additional metadata (no PII!)
|
|
170
|
+
"""
|
|
171
|
+
properties = {
|
|
172
|
+
"feature": feature,
|
|
173
|
+
"action": action,
|
|
174
|
+
}
|
|
175
|
+
if metadata:
|
|
176
|
+
# Sanitize metadata to ensure no PII
|
|
177
|
+
safe_metadata = {
|
|
178
|
+
k: v for k, v in metadata.items()
|
|
179
|
+
if k in ("count", "duration_ms", "template", "mode", "provider")
|
|
180
|
+
}
|
|
181
|
+
properties.update(safe_metadata)
|
|
182
|
+
|
|
183
|
+
track("feature_used", properties)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _queue_event(event: dict[str, Any]) -> None:
|
|
187
|
+
"""Queue an event locally for later sending."""
|
|
188
|
+
AUDIT_DIR.mkdir(parents=True, exist_ok=True)
|
|
189
|
+
|
|
190
|
+
# Load existing queue
|
|
191
|
+
queue = _load_queue()
|
|
192
|
+
|
|
193
|
+
# Add event
|
|
194
|
+
queue.append(event)
|
|
195
|
+
|
|
196
|
+
# Keep only last 100 events
|
|
197
|
+
if len(queue) > 100:
|
|
198
|
+
queue = queue[-100:]
|
|
199
|
+
|
|
200
|
+
# Save
|
|
201
|
+
TELEMETRY_QUEUE_FILE.write_text(json.dumps(queue, indent=2))
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _load_queue() -> list[dict[str, Any]]:
|
|
205
|
+
"""Load the telemetry queue."""
|
|
206
|
+
if not TELEMETRY_QUEUE_FILE.exists():
|
|
207
|
+
return []
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
return json.loads(TELEMETRY_QUEUE_FILE.read_text())
|
|
211
|
+
except (json.JSONDecodeError, IOError):
|
|
212
|
+
return []
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _try_flush_queue() -> None:
|
|
216
|
+
"""Try to send queued events (non-blocking, best effort)."""
|
|
217
|
+
queue = _load_queue()
|
|
218
|
+
if not queue:
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
# Send batch (timeout: 2 seconds to not block CLI)
|
|
223
|
+
with httpx.Client(timeout=2.0) as client:
|
|
224
|
+
response = client.post(
|
|
225
|
+
TELEMETRY_ENDPOINT,
|
|
226
|
+
json={"events": queue},
|
|
227
|
+
headers={"Content-Type": "application/json"},
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
if response.status_code == 200:
|
|
231
|
+
# Clear queue on success
|
|
232
|
+
clear_queue()
|
|
233
|
+
except Exception:
|
|
234
|
+
# Silently fail - telemetry should never break the CLI
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def clear_queue() -> int:
|
|
239
|
+
"""
|
|
240
|
+
Clear the telemetry queue.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Number of events cleared
|
|
244
|
+
"""
|
|
245
|
+
queue = _load_queue()
|
|
246
|
+
count = len(queue)
|
|
247
|
+
|
|
248
|
+
if TELEMETRY_QUEUE_FILE.exists():
|
|
249
|
+
TELEMETRY_QUEUE_FILE.unlink()
|
|
250
|
+
|
|
251
|
+
return count
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def get_queue_stats() -> dict[str, Any]:
|
|
255
|
+
"""
|
|
256
|
+
Get statistics about the telemetry queue.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Dict with queue statistics
|
|
260
|
+
"""
|
|
261
|
+
queue = _load_queue()
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
"enabled": is_telemetry_enabled(),
|
|
265
|
+
"pending_events": len(queue),
|
|
266
|
+
"device_id": get_device_id() if DEVICE_ID_FILE.exists() else None,
|
|
267
|
+
"endpoint": TELEMETRY_ENDPOINT,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def get_pending_events() -> list[dict[str, Any]]:
|
|
272
|
+
"""
|
|
273
|
+
Get all pending telemetry events (for transparency).
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
List of pending events
|
|
277
|
+
"""
|
|
278
|
+
return _load_queue()
|
|
279
|
+
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: repr-cli
|
|
3
|
+
Version: 0.2.6
|
|
4
|
+
Summary: A beautiful, privacy-first CLI that analyzes your code repositories and generates a compelling developer profile
|
|
5
|
+
Author-email: Repr <hello@repr.dev>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Repr
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://repr.dev
|
|
29
|
+
Project-URL: Documentation, https://repr.dev/docs
|
|
30
|
+
Project-URL: Repository, https://github.com/repr-app/cli
|
|
31
|
+
Keywords: cli,developer,profile,git,analysis,repr
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Environment :: Console
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: Operating System :: OS Independent
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
41
|
+
Requires-Python: >=3.10
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
License-File: LICENSE
|
|
44
|
+
Requires-Dist: typer>=0.9.0
|
|
45
|
+
Requires-Dist: rich>=13.0.0
|
|
46
|
+
Requires-Dist: gitpython>=3.1.0
|
|
47
|
+
Requires-Dist: pygments>=2.16.0
|
|
48
|
+
Requires-Dist: httpx>=0.25.0
|
|
49
|
+
Requires-Dist: openai>=1.0.0
|
|
50
|
+
Requires-Dist: keyring>=24.0.0
|
|
51
|
+
Provides-Extra: dev
|
|
52
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
53
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
54
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
55
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
56
|
+
Dynamic: license-file
|
|
57
|
+
|
|
58
|
+
# Repr CLI
|
|
59
|
+
|
|
60
|
+
**Stop trying to remember what you did.** Your git history is already a career diary—repr unlocks it.
|
|
61
|
+
|
|
62
|
+
Turn commits into professional narratives for interviews, performance reviews, and career growth. Local-first, privacy-focused, works offline.
|
|
63
|
+
|
|
64
|
+
[](https://pypi.org/project/repr-cli/)
|
|
65
|
+
[](https://pypi.org/project/repr-cli/)
|
|
66
|
+
[](https://opensource.org/licenses/MIT)
|
|
67
|
+
[](https://github.com/repr-app/cli/actions/workflows/build-release.yml)
|
|
68
|
+
|
|
69
|
+
## Real Developers, Real Results
|
|
70
|
+
|
|
71
|
+
> *"I used repr to prep for my Meta interview in 30 minutes. Turned 2 years of commits into 8 STAR-format stories. Nailed every behavioral question."*
|
|
72
|
+
> **— Sarah, Senior Backend Engineer**
|
|
73
|
+
|
|
74
|
+
> *"Our sprint demos went from chaos to polished in 5 minutes. Just run `repr since '2 weeks ago'` and export. Stakeholders love it."*
|
|
75
|
+
> **— Marcus, Engineering Manager**
|
|
76
|
+
|
|
77
|
+
> *"I run repr in a fully air-gapped environment. Zero network calls, 100% local. It's the only tool I trust for this."*
|
|
78
|
+
> **— Alex, Defense Contractor**
|
|
79
|
+
|
|
80
|
+
## Perfect For
|
|
81
|
+
|
|
82
|
+
- 🎯 **Interview Prep** — Generate STAR-format stories in 30 minutes instead of hours of commit archaeology
|
|
83
|
+
- 📊 **Performance Reviews** — Turn 6 months of work into quantified impact with one command
|
|
84
|
+
- 🚀 **Sprint Demos** — Professional changelogs for stakeholders in seconds
|
|
85
|
+
- 👔 **Weekly 1-on-1s** — Show up with specific examples instead of vague "I worked on stuff"
|
|
86
|
+
- 🔒 **Sensitive Work** — Air-gapped support for defense, healthcare, finance
|
|
87
|
+
- 💼 **Engineering Managers** — Prep for team reviews with per-developer summaries
|
|
88
|
+
|
|
89
|
+
## Why Repr
|
|
90
|
+
|
|
91
|
+
### Privacy First (Not an Afterthought)
|
|
92
|
+
|
|
93
|
+
- ✅ **Local-first by default** — Your repos, diffs, and stories stay on your machine in `~/.repr/`
|
|
94
|
+
- ✅ **Air-gapped ready** — Works in fully offline environments (defense, healthcare, finance approved)
|
|
95
|
+
- ✅ **Bring your own model** — Use local LLMs (Ollama) or your own API keys (OpenAI/Anthropic)
|
|
96
|
+
- ✅ **Privacy audit** — See exactly what data (if any) left your machine with `repr privacy audit`
|
|
97
|
+
- ✅ **OS keychain** — API keys never touch config files, stored in system keychain
|
|
98
|
+
- ✅ **Zero telemetry** — No tracking, no analytics, no silent uploads
|
|
99
|
+
|
|
100
|
+
### Time Savings
|
|
101
|
+
|
|
102
|
+
| Task | Without repr | With repr | Savings |
|
|
103
|
+
|------|--------------|-----------|---------|
|
|
104
|
+
| Interview prep | 3-4 hours digging through commits | 30 minutes | **85% faster** |
|
|
105
|
+
| Performance review | 2 days remembering work | 5 minutes | **99% faster** |
|
|
106
|
+
| Sprint demo prep | 30 min asking "what did we ship?" | 2 minutes | **93% faster** |
|
|
107
|
+
| Weekly 1-on-1 prep | 15 min trying to remember | 30 seconds | **97% faster** |
|
|
108
|
+
|
|
109
|
+
### vs. Alternatives
|
|
110
|
+
|
|
111
|
+
**vs. Manual brag documents:**
|
|
112
|
+
❌ Requires discipline to maintain
|
|
113
|
+
❌ Easy to forget to update
|
|
114
|
+
❌ No structure or templates
|
|
115
|
+
✅ repr: Automatic, retroactive, professional templates
|
|
116
|
+
|
|
117
|
+
**vs. GitHub commit history:**
|
|
118
|
+
❌ Raw commits are cryptic
|
|
119
|
+
❌ No narrative or context
|
|
120
|
+
❌ Not interview/resume ready
|
|
121
|
+
✅ repr: LLM transforms commits into narratives
|
|
122
|
+
|
|
123
|
+
**vs. Trying to remember at review time:**
|
|
124
|
+
❌ Forget 80% of your work
|
|
125
|
+
❌ Can't quantify impact
|
|
126
|
+
❌ Miss your best stories
|
|
127
|
+
✅ repr: Never forget, always quantified
|
|
128
|
+
|
|
129
|
+
## Install
|
|
130
|
+
|
|
131
|
+
### macOS / Linux (Homebrew)
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
brew tap repr-app/tap
|
|
135
|
+
brew install repr
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Direct Download
|
|
139
|
+
|
|
140
|
+
Grab pre-built binaries for macOS, Linux, and Windows from the [latest release](https://github.com/repr-app/cli/releases/latest).
|
|
141
|
+
|
|
142
|
+
### Python (pipx)
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pipx install repr-cli
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Quickstart (60 seconds)
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# 1) Scan your repos and set up local config
|
|
152
|
+
repr init ~/code
|
|
153
|
+
|
|
154
|
+
# 2) Generate stories from your recent work (local LLM)
|
|
155
|
+
repr generate --local
|
|
156
|
+
|
|
157
|
+
# 3) See what you created
|
|
158
|
+
repr stories
|
|
159
|
+
repr story view <id>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Common workflows
|
|
163
|
+
|
|
164
|
+
For full step-by-step guides, see the [documentation](https://repr.dev/docs/cli/workflows/). Below are the quick happy-path snippets.
|
|
165
|
+
|
|
166
|
+
### First-time setup
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
repr init ~/code
|
|
170
|
+
repr week
|
|
171
|
+
repr generate --local
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
[Full guide →](https://repr.dev/docs/cli/workflows/first-time-setup)
|
|
175
|
+
|
|
176
|
+
### Daily workflow
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
repr hooks install --all
|
|
180
|
+
repr generate --local
|
|
181
|
+
repr review
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
[Full guide →](https://repr.dev/docs/cli/workflows/daily-workflow)
|
|
185
|
+
|
|
186
|
+
### Weekly reflection
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
repr week
|
|
190
|
+
repr generate --local
|
|
191
|
+
repr story edit <id>
|
|
192
|
+
repr story feature <id>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
[Full guide →](https://repr.dev/docs/cli/workflows/weekly-reflection)
|
|
196
|
+
|
|
197
|
+
### Interview prep (STAR stories)
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
repr generate --template interview --local
|
|
201
|
+
repr stories
|
|
202
|
+
repr story view <id>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
[Full guide →](https://repr.dev/docs/cli/workflows/interview-prep)
|
|
206
|
+
|
|
207
|
+
### Publish your profile (optional)
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
repr login
|
|
211
|
+
repr push --dry-run
|
|
212
|
+
repr push --all
|
|
213
|
+
repr profile link
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
[Full guide →](https://repr.dev/docs/cli/workflows/publishing-profile)
|
|
217
|
+
|
|
218
|
+
### Privacy-focused (local only)
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
repr privacy lock-local
|
|
222
|
+
repr llm configure
|
|
223
|
+
repr llm test
|
|
224
|
+
repr generate --local
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
[Full guide →](https://repr.dev/docs/cli/workflows/privacy-local-only)
|
|
228
|
+
|
|
229
|
+
### Multi-device sync
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
repr login
|
|
233
|
+
repr sync
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
[Full guide →](https://repr.dev/docs/cli/workflows/multi-device-sync)
|
|
237
|
+
|
|
238
|
+
### Troubleshooting
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
repr status
|
|
242
|
+
repr mode
|
|
243
|
+
repr doctor
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
[Full guide →](https://repr.dev/docs/cli/workflows/troubleshooting)
|
|
247
|
+
|
|
248
|
+
## Configure your models
|
|
249
|
+
|
|
250
|
+
Your config lives at `~/.repr/config.json`.
|
|
251
|
+
|
|
252
|
+
### Local LLM (Ollama/LocalAI)
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
repr llm configure
|
|
256
|
+
|
|
257
|
+
# or set it manually:
|
|
258
|
+
repr config set llm.local_api_url http://localhost:11434/v1
|
|
259
|
+
repr config set llm.local_model llama3.2
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Bring your own API keys (BYOK)
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
repr llm add openai
|
|
266
|
+
repr llm add anthropic
|
|
267
|
+
repr llm use byok:openai
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Privacy modes
|
|
271
|
+
|
|
272
|
+
| Mode | Typical command | What happens |
|
|
273
|
+
|------|-----------------|--------------|
|
|
274
|
+
| **Local LLM** | `repr generate --local` | Talks only to your local endpoint. |
|
|
275
|
+
| **BYOK** | `repr llm add <provider>` | Calls your provider directly with your key. |
|
|
276
|
+
| **Cloud** | `repr generate --cloud` | Requires login; you initiate all network calls. |
|
|
277
|
+
| **Offline** | `repr week` / `repr stories` | Pure local operations. |
|
|
278
|
+
|
|
279
|
+
## Command help
|
|
280
|
+
|
|
281
|
+
For the full flag reference:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
repr --help
|
|
285
|
+
repr <command> --help
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Enterprise & Compliance
|
|
289
|
+
|
|
290
|
+
### Air-Gapped Environments
|
|
291
|
+
|
|
292
|
+
repr works in fully offline, air-gapped environments:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
# 1. Install repr (transfer binary via USB)
|
|
296
|
+
# 2. Install Ollama and download models offline
|
|
297
|
+
# 3. Lock to local-only permanently
|
|
298
|
+
repr privacy lock-local --permanent
|
|
299
|
+
|
|
300
|
+
# 4. Generate stories (zero network calls)
|
|
301
|
+
repr generate --local
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Use cases:**
|
|
305
|
+
- Defense contractors (classified environments)
|
|
306
|
+
- Healthcare (HIPAA compliance)
|
|
307
|
+
- Finance (SOX/PCI requirements)
|
|
308
|
+
- Stealth startups (pre-launch confidentiality)
|
|
309
|
+
|
|
310
|
+
### Privacy Audit Trail
|
|
311
|
+
|
|
312
|
+
See exactly what data left your machine:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
repr privacy audit --days 30
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Output:
|
|
319
|
+
```
|
|
320
|
+
Network Activity Audit (Last 30 days)
|
|
321
|
+
|
|
322
|
+
No network activity detected.
|
|
323
|
+
|
|
324
|
+
Local operations:
|
|
325
|
+
• 143 commits analyzed
|
|
326
|
+
• 23 stories generated
|
|
327
|
+
• 0 cloud syncs
|
|
328
|
+
• 0 API calls to repr.dev
|
|
329
|
+
|
|
330
|
+
Mode: LOCAL_ONLY (locked)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Perfect for security audits and compliance reviews.
|
|
334
|
+
|
|
335
|
+
### BYOK (Bring Your Own Key)
|
|
336
|
+
|
|
337
|
+
Use your own API keys, stored securely:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
# Keys stored in OS keychain (never in config files)
|
|
341
|
+
repr llm add openai
|
|
342
|
+
repr llm add anthropic
|
|
343
|
+
|
|
344
|
+
# Calls go directly to your provider, not repr.dev
|
|
345
|
+
repr generate # Uses your OpenAI key
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
- ✅ Keys stored in macOS Keychain / Windows Credential Manager / Linux Secret Service
|
|
349
|
+
- ✅ repr.dev never sees your keys or data
|
|
350
|
+
- ✅ Full control over costs and models
|
|
351
|
+
|
|
352
|
+
## Documentation
|
|
353
|
+
|
|
354
|
+
- **[Full Documentation](https://repr.dev/docs)** — Comprehensive guides
|
|
355
|
+
- **[USER_WORKFLOWS.md](docs/USER_WORKFLOWS.md)** — 10 detailed workflow examples
|
|
356
|
+
- **[Privacy Model](https://repr.dev/docs/concepts/privacy-model)** — How data is protected
|
|
357
|
+
|
|
358
|
+
## License
|
|
359
|
+
|
|
360
|
+
MIT License — see [LICENSE](LICENSE).
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
**🚀 Ready to unlock your git history?**
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
brew install repr
|
|
368
|
+
repr init ~/code
|
|
369
|
+
repr generate --local
|
|
370
|
+
```
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
repr/__init__.py,sha256=
|
|
1
|
+
repr/__init__.py,sha256=xlUANqgidfRx2ARl9tCwi--LhTgCtiLWXELOeCMT-34,446
|
|
2
2
|
repr/__main__.py,sha256=M0ECtxOrmmYoYrYV5XI9UhDnOjWThxn0-PPysKs3RT0,127
|
|
3
3
|
repr/api.py,sha256=Rr6MEUkjf7LJ6TcxbdVstfpUM_mDpTKhllbFwy9jK2w,11893
|
|
4
4
|
repr/auth.py,sha256=-tqd2MMgFlowbhAqLHeSnVpDBkintkZ4kmPDZmczQFU,11682
|
|
5
|
-
repr/cli.py,sha256=
|
|
5
|
+
repr/cli.py,sha256=X0oeeaJ8ygH6QGYJw8VyislEyjyDW0KSw24JPY1P3cw,77291
|
|
6
6
|
repr/config.py,sha256=GZf5ucrBFIfOo9UtKE-DAZ9Ns1suAKG0jvUAY64oGIc,30601
|
|
7
7
|
repr/discovery.py,sha256=2RYmJleqV7TbxIMMYP2izkEBUeKH7U1F-U4KAUlUNww,14816
|
|
8
8
|
repr/doctor.py,sha256=6cI21xXIlTNRzHi2fRfHpm__erO8jBZc6vge8-29ip4,13404
|
|
@@ -13,12 +13,13 @@ repr/llm.py,sha256=gTYloz7ONTpFQm73YFIVGOrjsk0iyocMTM4YkF4s4xI,14598
|
|
|
13
13
|
repr/openai_analysis.py,sha256=-9POoLF6B15_oBKJw_CjKH2DuWEIgIlOmtyjS4Gjbck,25764
|
|
14
14
|
repr/privacy.py,sha256=HITso2pzwN8R0Izh3SjUsrzcpjVw5bJEhbippAGeMiY,9795
|
|
15
15
|
repr/storage.py,sha256=72nfFcR2Y98vpSjaO7zVHisq_Ln2UrHmGyDhEqEmDjU,14863
|
|
16
|
+
repr/telemetry.py,sha256=7ANJJUB4Dd7A_HFVPqc92Gy77ruREzlmgayFQkwuC9s,6961
|
|
16
17
|
repr/templates.py,sha256=RQl7nUfy8IK6QFKzgpcebkBbQH0E_brbYh83pzym1TM,6530
|
|
17
18
|
repr/tools.py,sha256=QoGeti5Sye2wVuE-7UPxd_TDNXoen-xYfsFoT9rYRPs,20737
|
|
18
19
|
repr/ui.py,sha256=5jycUT-5Q0az4FFUzgarI8CfVAEEUPSEsT24Fad2kG8,3994
|
|
19
|
-
repr_cli-0.2.
|
|
20
|
-
repr_cli-0.2.
|
|
21
|
-
repr_cli-0.2.
|
|
22
|
-
repr_cli-0.2.
|
|
23
|
-
repr_cli-0.2.
|
|
24
|
-
repr_cli-0.2.
|
|
20
|
+
repr_cli-0.2.6.dist-info/licenses/LICENSE,sha256=tI16Ry3IQhjsde6weJ_in6czzWW2EF4Chz1uicyDLAA,1061
|
|
21
|
+
repr_cli-0.2.6.dist-info/METADATA,sha256=Aw-hTKrMYCBdqHqcSuxwJ37k4a06eac1OEzHCkpsLso,10898
|
|
22
|
+
repr_cli-0.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
repr_cli-0.2.6.dist-info/entry_points.txt,sha256=SJoKgNB-fRy6O2T_lztFr9T3ND_BQl0ijWxNW-J7dUU,38
|
|
24
|
+
repr_cli-0.2.6.dist-info/top_level.txt,sha256=LNgPqdJPQnlicRve7uzI4a6rEUdcxHrNkUq_2w7eeiA,5
|
|
25
|
+
repr_cli-0.2.6.dist-info/RECORD,,
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: repr-cli
|
|
3
|
-
Version: 0.2.4
|
|
4
|
-
Summary: A beautiful, privacy-first CLI that analyzes your code repositories and generates a compelling developer profile
|
|
5
|
-
Author-email: Repr <hello@repr.dev>
|
|
6
|
-
License: MIT License
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2024 Repr
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
28
|
-
Project-URL: Homepage, https://repr.dev
|
|
29
|
-
Project-URL: Documentation, https://repr.dev/docs
|
|
30
|
-
Project-URL: Repository, https://github.com/repr-app/cli
|
|
31
|
-
Keywords: cli,developer,profile,git,analysis,repr
|
|
32
|
-
Classifier: Development Status :: 4 - Beta
|
|
33
|
-
Classifier: Environment :: Console
|
|
34
|
-
Classifier: Intended Audience :: Developers
|
|
35
|
-
Classifier: Operating System :: OS Independent
|
|
36
|
-
Classifier: Programming Language :: Python :: 3
|
|
37
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
-
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
41
|
-
Requires-Python: >=3.10
|
|
42
|
-
Description-Content-Type: text/markdown
|
|
43
|
-
License-File: LICENSE
|
|
44
|
-
Requires-Dist: typer>=0.9.0
|
|
45
|
-
Requires-Dist: rich>=13.0.0
|
|
46
|
-
Requires-Dist: gitpython>=3.1.0
|
|
47
|
-
Requires-Dist: pygments>=2.16.0
|
|
48
|
-
Requires-Dist: httpx>=0.25.0
|
|
49
|
-
Requires-Dist: openai>=1.0.0
|
|
50
|
-
Requires-Dist: keyring>=24.0.0
|
|
51
|
-
Provides-Extra: dev
|
|
52
|
-
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
53
|
-
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
54
|
-
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
55
|
-
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
56
|
-
Dynamic: license-file
|
|
57
|
-
|
|
58
|
-
# Repr CLI
|
|
59
|
-
|
|
60
|
-
A local-first developer tool that turns your git commit history into professional narratives.
|
|
61
|
-
|
|
62
|
-
**Privacy Guarantee:** Zero data leaves your machine unless you explicitly publish. Your keys, your models, your data.
|
|
63
|
-
|
|
64
|
-
[](https://pypi.org/project/repr-cli/)
|
|
65
|
-
[](https://pypi.org/project/repr-cli/)
|
|
66
|
-
[](https://opensource.org/licenses/MIT)
|
|
67
|
-
[](https://github.com/repr-app/cli/actions/workflows/build-release.yml)
|
|
68
|
-
|
|
69
|
-
## Philosophy
|
|
70
|
-
|
|
71
|
-
Repr is built on three core principles:
|
|
72
|
-
|
|
73
|
-
1. **Local-First:** Analysis happens on your machine. You own the output.
|
|
74
|
-
2. **Bring Your Own Keys:** Use your own OpenAI API key or a local LLM (Ollama, etc.). We don't sit in the middle.
|
|
75
|
-
3. **Opt-In Cloud:** Publishing to `repr.dev` is strictly optional. Use it for backup or sharing, or ignore it entirely.
|
|
76
|
-
|
|
77
|
-
## Installation
|
|
78
|
-
|
|
79
|
-
### macOS / Linux (Homebrew)
|
|
80
|
-
```bash
|
|
81
|
-
brew tap repr-app/tap
|
|
82
|
-
brew install repr
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Direct Download
|
|
86
|
-
Download pre-built binaries for macOS, Linux, and Windows from the [latest release](https://github.com/repr-app/cli/releases/latest).
|
|
87
|
-
|
|
88
|
-
### Python (pipx)
|
|
89
|
-
```bash
|
|
90
|
-
pipx install repr-cli
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## The Local Workflow
|
|
94
|
-
|
|
95
|
-
You can use Repr entirely offline or with your own API keys. No account required.
|
|
96
|
-
|
|
97
|
-
### 1. Initialize
|
|
98
|
-
Scan for repositories and set up local config.
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
repr init ~/code
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### 2. Generate stories
|
|
105
|
-
Create stories from your commit history.
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
# Using a local LLM (e.g., Ollama running Llama 3)
|
|
109
|
-
repr generate --local
|
|
110
|
-
|
|
111
|
-
# Or configure your own API key (BYOK)
|
|
112
|
-
repr llm add openai
|
|
113
|
-
repr generate
|
|
114
|
-
|
|
115
|
-
# Or use cloud (requires login)
|
|
116
|
-
repr login
|
|
117
|
-
repr generate --cloud
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
This reads your git log, diffs, and file context to generate meaningful summaries of your work. All processing happens locally or directly against the API you specify.
|
|
121
|
-
|
|
122
|
-
### 3. View your stories
|
|
123
|
-
Inspect the generated stories stored on your machine.
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
repr stories
|
|
127
|
-
repr story view <id>
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Output is stored in `~/.repr/`, staying fully under your control.
|
|
131
|
-
|
|
132
|
-
### 4. Track multiple repositories
|
|
133
|
-
Configure Repr to watch multiple projects.
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
repr repos add ~/code/work-project
|
|
137
|
-
repr repos add ~/code/side-project
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## Optional: Cloud & Publishing
|
|
141
|
-
|
|
142
|
-
If you want convenience without managing your own API keys, you can use Repr's backend. It processes your code with proprietary models but operates under a **zero data retention (ZDR) policy**—no logging, no storage beyond ephemeral processing.
|
|
143
|
-
|
|
144
|
-
Alternatively, use the cloud for backup and sharing: sync your locally generated stories or create a public profile (e.g., `repr.dev/yourname`).
|
|
145
|
-
|
|
146
|
-
**This is the only time data leaves your machine.**
|
|
147
|
-
|
|
148
|
-
### 1. Authenticate
|
|
149
|
-
```bash
|
|
150
|
-
repr login
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### 2. Push stories
|
|
154
|
-
Publish your locally generated stories to your profile.
|
|
155
|
-
|
|
156
|
-
```bash
|
|
157
|
-
repr push
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### 3. Sync across devices
|
|
161
|
-
Keep your stories in sync.
|
|
162
|
-
|
|
163
|
-
```bash
|
|
164
|
-
repr sync
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### 4. Automate (Optional)
|
|
168
|
-
Install git hooks to automatically track new commits as you work.
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
repr hooks install --all
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## Configuration
|
|
175
|
-
|
|
176
|
-
Repr respects standard environment variables and local configuration.
|
|
177
|
-
|
|
178
|
-
**Config file:** `~/.repr/config.json`
|
|
179
|
-
|
|
180
|
-
### Using Local LLMs (Ollama, LocalAI)
|
|
181
|
-
Point Repr to any OpenAI-compatible endpoint:
|
|
182
|
-
|
|
183
|
-
```bash
|
|
184
|
-
repr llm configure
|
|
185
|
-
# Or manually:
|
|
186
|
-
repr config set llm.local_api_url http://localhost:11434/v1
|
|
187
|
-
repr config set llm.local_model llama3.2
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Bring Your Own Key (BYOK)
|
|
191
|
-
Configure your own API keys:
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
repr llm add openai
|
|
195
|
-
repr llm add anthropic
|
|
196
|
-
repr llm use byok:openai
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Privacy Modes
|
|
200
|
-
|
|
201
|
-
| Mode | Command | Behavior |
|
|
202
|
-
|------|---------|----------|
|
|
203
|
-
| **Local LLM** | `repr generate --local` | Uses your local LLM endpoint (Ollama, etc.). Zero external network calls. |
|
|
204
|
-
| **BYOK** | `repr llm add <provider>` | Configure your own API keys (OpenAI, Anthropic, etc.). Direct connection, no middleman. |
|
|
205
|
-
| **Cloud** | `repr generate --cloud` | **(Requires Login)** Uses Repr's backend. Sends metadata + diffs. Zero data retention (ZDR) policy—no logging, ephemeral processing only. |
|
|
206
|
-
| **Offline** | Local operations only | Works without network. View stories, repos, export profiles. |
|
|
207
|
-
|
|
208
|
-
## Command Reference
|
|
209
|
-
|
|
210
|
-
### Getting Started
|
|
211
|
-
- `repr init [path]` - Initialize repr, scan for repositories
|
|
212
|
-
- `repr login` - Authenticate with repr.dev
|
|
213
|
-
- `repr whoami` - Show current user
|
|
214
|
-
- `repr logout` - Sign out
|
|
215
|
-
- `repr status` - Show overall status and health
|
|
216
|
-
- `repr mode` - Show current execution mode
|
|
217
|
-
- `repr doctor` - Run health checks and diagnostics
|
|
218
|
-
|
|
219
|
-
### Generation & Analysis
|
|
220
|
-
- `repr generate [--local|--cloud]` - Generate stories from commits
|
|
221
|
-
- `--repo <path>` - Generate for specific repo
|
|
222
|
-
- `--commits <sha1,sha2>` - Generate from specific commits
|
|
223
|
-
- `--template <name>` - Use template (resume, changelog, narrative, interview)
|
|
224
|
-
- `--dry-run` - Preview what would be sent
|
|
225
|
-
- `repr week [--save]` - Show work from this week
|
|
226
|
-
- `repr since <date> [--save]` - Show work since a date
|
|
227
|
-
- `repr standup [--days N]` - Quick summary for standup
|
|
228
|
-
|
|
229
|
-
### Stories
|
|
230
|
-
- `repr stories [--repo NAME] [--needs-review]` - List all stories
|
|
231
|
-
- `repr story <action> <id>` - Manage a story (view, edit, delete, hide, feature)
|
|
232
|
-
- `repr review` - Interactive review workflow
|
|
233
|
-
- `repr commits [--repo NAME] [--limit N]` - List recent commits
|
|
234
|
-
|
|
235
|
-
### Cloud Operations
|
|
236
|
-
- `repr push [--story ID] [--all]` - Publish stories to repr.dev
|
|
237
|
-
- `repr sync` - Sync stories across devices
|
|
238
|
-
- `repr pull` - Pull remote stories to local
|
|
239
|
-
|
|
240
|
-
### Repositories
|
|
241
|
-
- `repr repos [list|add|remove|pause|resume] [path]` - Manage tracked repos
|
|
242
|
-
|
|
243
|
-
### Git Hooks
|
|
244
|
-
- `repr hooks install [--all|--repo PATH]` - Install post-commit hooks
|
|
245
|
-
- `repr hooks remove [--all|--repo PATH]` - Remove hooks
|
|
246
|
-
- `repr hooks status` - Show hook status
|
|
247
|
-
|
|
248
|
-
### LLM Configuration
|
|
249
|
-
- `repr llm add <provider>` - Configure BYOK provider (openai, anthropic, groq, together)
|
|
250
|
-
- `repr llm remove <provider>` - Remove BYOK provider
|
|
251
|
-
- `repr llm use <mode>` - Set default mode (local, cloud, byok:<provider>)
|
|
252
|
-
- `repr llm configure` - Configure local LLM interactively
|
|
253
|
-
- `repr llm test` - Test LLM connection
|
|
254
|
-
|
|
255
|
-
### Privacy
|
|
256
|
-
- `repr privacy explain` - Show privacy guarantees
|
|
257
|
-
- `repr privacy audit [--days N]` - Show what data was sent to cloud
|
|
258
|
-
- `repr privacy lock-local [--permanent]` - Disable cloud features
|
|
259
|
-
- `repr privacy unlock-local` - Re-enable cloud features
|
|
260
|
-
|
|
261
|
-
### Configuration
|
|
262
|
-
- `repr config show [key]` - Display configuration
|
|
263
|
-
- `repr config set <key> <value>` - Set configuration value
|
|
264
|
-
- `repr config edit` - Open config in $EDITOR
|
|
265
|
-
|
|
266
|
-
### Data Management
|
|
267
|
-
- `repr data` - Show local storage info
|
|
268
|
-
- `repr data backup [--output FILE]` - Backup all local data
|
|
269
|
-
- `repr data restore <file> [--merge]` - Restore from backup
|
|
270
|
-
- `repr data clear-cache` - Clear local cache
|
|
271
|
-
|
|
272
|
-
### Profile
|
|
273
|
-
- `repr profile` - View profile
|
|
274
|
-
- `repr profile update [--preview]` - Generate/update profile from stories
|
|
275
|
-
- `repr profile set-bio <text>` - Set profile bio
|
|
276
|
-
- `repr profile set-location <text>` - Set location
|
|
277
|
-
- `repr profile set-available <bool>` - Set availability status
|
|
278
|
-
- `repr profile export [--format FORMAT]` - Export profile (md, json)
|
|
279
|
-
- `repr profile link` - Get shareable profile link
|
|
280
|
-
|
|
281
|
-
## License
|
|
282
|
-
|
|
283
|
-
MIT License - see [LICENSE](LICENSE).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|