repr-cli 0.2.21__tar.gz → 0.2.23__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 (72) hide show
  1. {repr_cli-0.2.21 → repr_cli-0.2.23}/PKG-INFO +1 -1
  2. {repr_cli-0.2.21 → repr_cli-0.2.23}/pyproject.toml +1 -1
  3. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/change_synthesis.py +39 -17
  4. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/cli.py +65 -22
  5. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/session_extractor.py +18 -7
  6. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/story_synthesis.py +37 -15
  7. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr_cli.egg-info/PKG-INFO +1 -1
  8. {repr_cli-0.2.21 → repr_cli-0.2.23}/LICENSE +0 -0
  9. {repr_cli-0.2.21 → repr_cli-0.2.23}/README.md +0 -0
  10. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/__init__.py +0 -0
  11. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/__main__.py +0 -0
  12. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/api.py +0 -0
  13. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/auth.py +0 -0
  14. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/config.py +0 -0
  15. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/configure.py +0 -0
  16. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/cron.py +0 -0
  17. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/__init__.py +0 -0
  18. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/build.py +0 -0
  19. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-B-aCjaCw.js +0 -0
  20. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-BYFVbEev.css +0 -0
  21. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-BrrhyJFO.css +0 -0
  22. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-C7Gzxc4f.js +0 -0
  23. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-CQdMXo6g.js +0 -0
  24. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-CcEg74ts.js +0 -0
  25. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-Cerc-iA_.js +0 -0
  26. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-CjVcBW2L.css +0 -0
  27. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-Cs8ofFGd.js +0 -0
  28. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-Dfl3mR5E.js +0 -0
  29. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-DwN0SeMc.css +0 -0
  30. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/assets/index-YFch_e0S.js +0 -0
  31. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/favicon.svg +0 -0
  32. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/dist/index.html +0 -0
  33. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/manager.py +0 -0
  34. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/dashboard/server.py +0 -0
  35. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/db.py +0 -0
  36. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/discovery.py +0 -0
  37. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/doctor.py +0 -0
  38. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/extractor.py +0 -0
  39. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/hooks.py +0 -0
  40. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/keychain.py +0 -0
  41. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/llm.py +0 -0
  42. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/loaders/__init__.py +0 -0
  43. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/loaders/base.py +0 -0
  44. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/loaders/claude_code.py +0 -0
  45. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/loaders/clawdbot.py +0 -0
  46. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/loaders/gemini_antigravity.py +0 -0
  47. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/mcp_server.py +0 -0
  48. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/models.py +0 -0
  49. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/openai_analysis.py +0 -0
  50. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/privacy.py +0 -0
  51. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/storage.py +0 -0
  52. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/telemetry.py +0 -0
  53. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/templates.py +0 -0
  54. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/timeline.py +0 -0
  55. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/tools.py +0 -0
  56. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/ui.py +0 -0
  57. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr/updater.py +0 -0
  58. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr_cli.egg-info/SOURCES.txt +0 -0
  59. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr_cli.egg-info/dependency_links.txt +0 -0
  60. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr_cli.egg-info/entry_points.txt +0 -0
  61. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr_cli.egg-info/requires.txt +0 -0
  62. {repr_cli-0.2.21 → repr_cli-0.2.23}/repr_cli.egg-info/top_level.txt +0 -0
  63. {repr_cli-0.2.21 → repr_cli-0.2.23}/setup.cfg +0 -0
  64. {repr_cli-0.2.21 → repr_cli-0.2.23}/setup.py +0 -0
  65. {repr_cli-0.2.21 → repr_cli-0.2.23}/tests/test_deduplication.py +0 -0
  66. {repr_cli-0.2.21 → repr_cli-0.2.23}/tests/test_environment_variables.py +0 -0
  67. {repr_cli-0.2.21 → repr_cli-0.2.23}/tests/test_network_sandboxing.py +0 -0
  68. {repr_cli-0.2.21 → repr_cli-0.2.23}/tests/test_privacy_guarantees.py +0 -0
  69. {repr_cli-0.2.21 → repr_cli-0.2.23}/tests/test_profile_export.py +0 -0
  70. {repr_cli-0.2.21 → repr_cli-0.2.23}/tests/test_repo_identity.py +0 -0
  71. {repr_cli-0.2.21 → repr_cli-0.2.23}/tests/test_stories_review.py +0 -0
  72. {repr_cli-0.2.21 → repr_cli-0.2.23}/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.21
3
+ Version: 0.2.23
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.21"
7
+ version = "0.2.23"
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"}
@@ -418,14 +418,25 @@ async def explain_group(
418
418
  changes=changes_str,
419
419
  )
420
420
 
421
- response = await client.chat.completions.create(
422
- model=model,
423
- messages=[
424
- {"role": "system", "content": GROUP_EXPLAIN_SYSTEM},
425
- {"role": "user", "content": prompt},
426
- ],
427
- temperature=0.7,
428
- )
421
+ async def make_request(use_temperature: bool = True):
422
+ kwargs = {
423
+ "model": model,
424
+ "messages": [
425
+ {"role": "system", "content": GROUP_EXPLAIN_SYSTEM},
426
+ {"role": "user", "content": prompt},
427
+ ],
428
+ }
429
+ if use_temperature:
430
+ kwargs["temperature"] = 0.7
431
+ return await client.chat.completions.create(**kwargs)
432
+
433
+ try:
434
+ response = await make_request(use_temperature=True)
435
+ except Exception as e:
436
+ if "temperature" in str(e).lower() and "unsupported" in str(e).lower():
437
+ response = await make_request(use_temperature=False)
438
+ else:
439
+ raise
429
440
 
430
441
  return response.choices[0].message.content.strip()
431
442
 
@@ -455,15 +466,26 @@ async def synthesize_changes(
455
466
  unpushed=format_commit_changes(report.unpushed),
456
467
  )
457
468
 
458
- response = await client.chat.completions.create(
459
- model=model,
460
- messages=[
461
- {"role": "system", "content": CHANGE_SYNTHESIS_SYSTEM},
462
- {"role": "user", "content": prompt},
463
- ],
464
- response_format={"type": "json_object"},
465
- temperature=0.7,
466
- )
469
+ async def make_synthesis_request(use_temperature: bool = True):
470
+ kwargs = {
471
+ "model": model,
472
+ "messages": [
473
+ {"role": "system", "content": CHANGE_SYNTHESIS_SYSTEM},
474
+ {"role": "user", "content": prompt},
475
+ ],
476
+ "response_format": {"type": "json_object"},
477
+ }
478
+ if use_temperature:
479
+ kwargs["temperature"] = 0.7
480
+ return await client.chat.completions.create(**kwargs)
481
+
482
+ try:
483
+ response = await make_synthesis_request(use_temperature=True)
484
+ except Exception as e:
485
+ if "temperature" in str(e).lower() and "unsupported" in str(e).lower():
486
+ response = await make_synthesis_request(use_temperature=False)
487
+ else:
488
+ raise
467
489
 
468
490
  content = response.choices[0].message.content
469
491
  data = json.loads(content)
@@ -1031,7 +1031,11 @@ def generate(
1031
1031
 
1032
1032
  if not repo_commits:
1033
1033
  if not json_output:
1034
- console.print(f" No matching commits found")
1034
+ if commits:
1035
+ console.print(f" [dim]No commits matching specified SHAs[/]")
1036
+ else:
1037
+ filter_days_used = days if days else 90
1038
+ console.print(f" [dim]No commits in the last {filter_days_used} days[/]")
1035
1039
  continue
1036
1040
 
1037
1041
  # Filter out commits that are already part of existing stories (unless --force)
@@ -6071,16 +6075,29 @@ def create_branch(
6071
6075
 
6072
6076
  prompt = COMMIT_MESSAGE_USER.format(changes=changes_str)
6073
6077
 
6074
- with create_spinner("Generating branch name..."):
6075
- response = asyncio.run(client.chat.completions.create(
6076
- model="gpt-4o-mini",
6077
- messages=[
6078
+ async def make_branch_request(use_temperature: bool = True):
6079
+ kwargs = {
6080
+ "model": "gpt-4o-mini",
6081
+ "messages": [
6078
6082
  {"role": "system", "content": COMMIT_MESSAGE_SYSTEM},
6079
6083
  {"role": "user", "content": prompt},
6080
6084
  ],
6081
- response_format={"type": "json_object"},
6082
- temperature=0.3,
6083
- ))
6085
+ "response_format": {"type": "json_object"},
6086
+ }
6087
+ if use_temperature:
6088
+ kwargs["temperature"] = 0.3
6089
+ return await client.chat.completions.create(**kwargs)
6090
+
6091
+ async def get_branch_response():
6092
+ try:
6093
+ return await make_branch_request(use_temperature=True)
6094
+ except Exception as e:
6095
+ if "temperature" in str(e).lower() and "unsupported" in str(e).lower():
6096
+ return await make_branch_request(use_temperature=False)
6097
+ raise
6098
+
6099
+ with create_spinner("Generating branch name..."):
6100
+ response = asyncio.run(get_branch_response())
6084
6101
  data = json.loads(response.choices[0].message.content)
6085
6102
  branch_name = data.get("branch", "")
6086
6103
  commit_msg = data.get("message", "")
@@ -6223,16 +6240,29 @@ def commit_staged(
6223
6240
  changes_str = format_file_changes(staged)
6224
6241
  prompt = COMMIT_MESSAGE_USER.format(changes=changes_str)
6225
6242
 
6226
- with create_spinner("Generating commit message..."):
6227
- response = asyncio.run(client.chat.completions.create(
6228
- model="gpt-4o-mini",
6229
- messages=[
6243
+ async def make_commit_request(use_temperature: bool = True):
6244
+ kwargs = {
6245
+ "model": "gpt-4o-mini",
6246
+ "messages": [
6230
6247
  {"role": "system", "content": COMMIT_MESSAGE_SYSTEM},
6231
6248
  {"role": "user", "content": prompt},
6232
6249
  ],
6233
- response_format={"type": "json_object"},
6234
- temperature=0.3,
6235
- ))
6250
+ "response_format": {"type": "json_object"},
6251
+ }
6252
+ if use_temperature:
6253
+ kwargs["temperature"] = 0.3
6254
+ return await client.chat.completions.create(**kwargs)
6255
+
6256
+ async def get_commit_response():
6257
+ try:
6258
+ return await make_commit_request(use_temperature=True)
6259
+ except Exception as e:
6260
+ if "temperature" in str(e).lower() and "unsupported" in str(e).lower():
6261
+ return await make_commit_request(use_temperature=False)
6262
+ raise
6263
+
6264
+ with create_spinner("Generating commit message..."):
6265
+ response = asyncio.run(get_commit_response())
6236
6266
  data = json.loads(response.choices[0].message.content)
6237
6267
  branch_name = data.get("branch", "")
6238
6268
  commit_msg = data.get("message", "")
@@ -6661,16 +6691,29 @@ def create_pr(
6661
6691
 
6662
6692
  prompt = PR_USER.format(commits=commits_text)
6663
6693
 
6664
- with create_spinner("Generating PR..."):
6665
- response = asyncio.run(client.chat.completions.create(
6666
- model="gpt-4o-mini",
6667
- messages=[
6694
+ async def make_pr_request(use_temperature: bool = True):
6695
+ kwargs = {
6696
+ "model": "gpt-4o-mini",
6697
+ "messages": [
6668
6698
  {"role": "system", "content": PR_SYSTEM},
6669
6699
  {"role": "user", "content": prompt},
6670
6700
  ],
6671
- response_format={"type": "json_object"},
6672
- temperature=0.3,
6673
- ))
6701
+ "response_format": {"type": "json_object"},
6702
+ }
6703
+ if use_temperature:
6704
+ kwargs["temperature"] = 0.3
6705
+ return await client.chat.completions.create(**kwargs)
6706
+
6707
+ async def get_pr_response():
6708
+ try:
6709
+ return await make_pr_request(use_temperature=True)
6710
+ except Exception as e:
6711
+ if "temperature" in str(e).lower() and "unsupported" in str(e).lower():
6712
+ return await make_pr_request(use_temperature=False)
6713
+ raise
6714
+
6715
+ with create_spinner("Generating PR..."):
6716
+ response = asyncio.run(get_pr_response())
6674
6717
  data = json.loads(response.choices[0].message.content)
6675
6718
  pr_title = data.get("title", current_branch)
6676
6719
  pr_body = data.get("body", "")
@@ -319,18 +319,29 @@ class SessionExtractor:
319
319
  # Call LLM (using sync client to avoid event loop cleanup issues)
320
320
  client = self._get_client()
321
321
 
322
- try:
323
- response = client.chat.completions.create(
324
- model=self.model.split("/")[-1] if "/" in self.model else self.model,
325
- messages=[
322
+ def make_extraction_request(use_temperature: bool = True):
323
+ kwargs = {
324
+ "model": self.model.split("/")[-1] if "/" in self.model else self.model,
325
+ "messages": [
326
326
  {"role": "system", "content": EXTRACTION_SYSTEM_PROMPT},
327
327
  {"role": "user", "content": EXTRACTION_USER_PROMPT.format(
328
328
  session_transcript=transcript
329
329
  )},
330
330
  ],
331
- response_format={"type": "json_object"},
332
- temperature=0.3,
333
- )
331
+ "response_format": {"type": "json_object"},
332
+ }
333
+ if use_temperature:
334
+ kwargs["temperature"] = 0.3
335
+ return client.chat.completions.create(**kwargs)
336
+
337
+ try:
338
+ try:
339
+ response = make_extraction_request(use_temperature=True)
340
+ except Exception as e:
341
+ if "temperature" in str(e).lower() and "unsupported" in str(e).lower():
342
+ response = make_extraction_request(use_temperature=False)
343
+ else:
344
+ raise
334
345
 
335
346
  # Parse response
336
347
  content = response.choices[0].message.content
@@ -648,18 +648,29 @@ class StorySynthesizer:
648
648
  client = self._get_client()
649
649
  commits_text = self._format_commits_for_prompt(commits)
650
650
 
651
- try:
652
- response = client.chat.completions.create(
653
- model=self.model.split("/")[-1] if "/" in self.model else self.model,
654
- messages=[
651
+ def make_request(use_temperature: bool = True):
652
+ kwargs = {
653
+ "model": self.model.split("/")[-1] if "/" in self.model else self.model,
654
+ "messages": [
655
655
  {"role": "system", "content": STORY_SYNTHESIS_SYSTEM},
656
656
  {"role": "user", "content": STORY_SYNTHESIS_USER.format(
657
657
  commits_text=commits_text
658
658
  )},
659
659
  ],
660
- response_format={"type": "json_object"},
661
- temperature=0.3,
662
- )
660
+ "response_format": {"type": "json_object"},
661
+ }
662
+ if use_temperature:
663
+ kwargs["temperature"] = 0.3
664
+ return client.chat.completions.create(**kwargs)
665
+
666
+ try:
667
+ try:
668
+ response = make_request(use_temperature=True)
669
+ except Exception as e:
670
+ if "temperature" in str(e).lower() and "unsupported" in str(e).lower():
671
+ response = make_request(use_temperature=False)
672
+ else:
673
+ raise
663
674
 
664
675
  content = response.choices[0].message.content
665
676
 
@@ -1061,17 +1072,28 @@ async def transform_story_for_feed(
1061
1072
  )
1062
1073
  response_model = InternalStory
1063
1074
 
1064
- try:
1065
- # Use sync client to avoid event loop cleanup issues
1066
- response = client.chat.completions.create(
1067
- model=model_name.split("/")[-1] if "/" in model_name else model_name,
1068
- messages=[
1075
+ def make_story_request(use_temperature: bool = True):
1076
+ kwargs = {
1077
+ "model": model_name.split("/")[-1] if "/" in model_name else model_name,
1078
+ "messages": [
1069
1079
  {"role": "system", "content": system_prompt},
1070
1080
  {"role": "user", "content": user_prompt},
1071
1081
  ],
1072
- response_format={"type": "json_object"},
1073
- temperature=0.7,
1074
- )
1082
+ "response_format": {"type": "json_object"},
1083
+ }
1084
+ if use_temperature:
1085
+ kwargs["temperature"] = 0.7
1086
+ return client.chat.completions.create(**kwargs)
1087
+
1088
+ try:
1089
+ # Use sync client to avoid event loop cleanup issues
1090
+ try:
1091
+ response = make_story_request(use_temperature=True)
1092
+ except Exception as e:
1093
+ if "temperature" in str(e).lower() and "unsupported" in str(e).lower():
1094
+ response = make_story_request(use_temperature=False)
1095
+ else:
1096
+ raise
1075
1097
 
1076
1098
  content = response.choices[0].message.content.strip()
1077
1099
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repr-cli
3
- Version: 0.2.21
3
+ Version: 0.2.23
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
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes