repr-cli 0.2.11__tar.gz → 0.2.12__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.11/repr_cli.egg-info → repr_cli-0.2.12}/PKG-INFO +1 -1
  2. {repr_cli-0.2.11 → repr_cli-0.2.12}/pyproject.toml +1 -1
  3. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/__main__.py +2 -0
  4. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/doctor.py +2 -0
  5. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/hooks.py +99 -1
  6. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/llm.py +2 -0
  7. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/privacy.py +2 -0
  8. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/telemetry.py +2 -0
  9. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/templates.py +60 -56
  10. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/ui.py +2 -0
  11. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/updater.py +2 -0
  12. {repr_cli-0.2.11 → repr_cli-0.2.12/repr_cli.egg-info}/PKG-INFO +1 -1
  13. {repr_cli-0.2.11 → repr_cli-0.2.12}/setup.py +2 -0
  14. {repr_cli-0.2.11 → repr_cli-0.2.12}/LICENSE +0 -0
  15. {repr_cli-0.2.11 → repr_cli-0.2.12}/README.md +0 -0
  16. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/__init__.py +0 -0
  17. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/api.py +0 -0
  18. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/auth.py +0 -0
  19. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/cli.py +0 -0
  20. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/config.py +0 -0
  21. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/discovery.py +0 -0
  22. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/extractor.py +0 -0
  23. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/keychain.py +0 -0
  24. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/openai_analysis.py +0 -0
  25. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/storage.py +0 -0
  26. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr/tools.py +0 -0
  27. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr_cli.egg-info/SOURCES.txt +0 -0
  28. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr_cli.egg-info/dependency_links.txt +0 -0
  29. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr_cli.egg-info/entry_points.txt +0 -0
  30. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr_cli.egg-info/requires.txt +0 -0
  31. {repr_cli-0.2.11 → repr_cli-0.2.12}/repr_cli.egg-info/top_level.txt +0 -0
  32. {repr_cli-0.2.11 → repr_cli-0.2.12}/setup.cfg +0 -0
  33. {repr_cli-0.2.11 → repr_cli-0.2.12}/tests/test_environment_variables.py +0 -0
  34. {repr_cli-0.2.11 → repr_cli-0.2.12}/tests/test_network_sandboxing.py +0 -0
  35. {repr_cli-0.2.11 → repr_cli-0.2.12}/tests/test_privacy_guarantees.py +0 -0
  36. {repr_cli-0.2.11 → repr_cli-0.2.12}/tests/test_profile_export.py +0 -0
  37. {repr_cli-0.2.11 → repr_cli-0.2.12}/tests/test_repo_identity.py +0 -0
  38. {repr_cli-0.2.11 → repr_cli-0.2.12}/tests/test_stories_review.py +0 -0
  39. {repr_cli-0.2.11 → repr_cli-0.2.12}/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.11
3
+ Version: 0.2.12
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.11"
7
+ version = "0.2.12"
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"}
@@ -38,6 +38,8 @@ if __name__ == "__main__":
38
38
 
39
39
 
40
40
 
41
+
42
+
41
43
 
42
44
 
43
45
 
@@ -490,6 +490,8 @@ def run_all_checks() -> DoctorReport:
490
490
 
491
491
 
492
492
 
493
+
494
+
493
495
 
494
496
 
495
497
 
@@ -423,6 +423,8 @@ def queue_commit(repo_path: Path, commit_sha: str, message: str | None = None) -
423
423
  """Add a commit to the queue.
424
424
 
425
425
  Uses file locking to handle concurrent commits safely.
426
+ If auto_generate_on_hook is enabled and queue size meets batch_size,
427
+ triggers background story generation.
426
428
 
427
429
  Args:
428
430
  repo_path: Path to repository root
@@ -435,6 +437,8 @@ def queue_commit(repo_path: Path, commit_sha: str, message: str | None = None) -
435
437
  queue_path = get_queue_path(repo_path)
436
438
  lock_path = queue_path.with_suffix(".lock")
437
439
 
440
+ queue_size = 0
441
+
438
442
  try:
439
443
  fd = _acquire_lock(lock_path)
440
444
  try:
@@ -452,7 +456,7 @@ def queue_commit(repo_path: Path, commit_sha: str, message: str | None = None) -
452
456
  })
453
457
 
454
458
  save_queue(repo_path, queue)
455
- return True
459
+ queue_size = len(queue)
456
460
 
457
461
  finally:
458
462
  _release_lock(fd)
@@ -460,6 +464,100 @@ def queue_commit(repo_path: Path, commit_sha: str, message: str | None = None) -
460
464
  except QueueLockError:
461
465
  # Could not get lock, skip queuing
462
466
  return False
467
+
468
+ # Check if we should auto-generate stories
469
+ _maybe_auto_generate(repo_path, queue_size)
470
+
471
+ return True
472
+
473
+
474
+ def _maybe_auto_generate(repo_path: Path, queue_size: int) -> None:
475
+ """Check if auto-generation should be triggered and spawn background process.
476
+
477
+ Args:
478
+ repo_path: Path to repository root
479
+ queue_size: Current number of commits in queue
480
+ """
481
+ from .config import load_config
482
+
483
+ config = load_config()
484
+ generation_config = config.get("generation", {})
485
+
486
+ # Check if auto-generation is enabled
487
+ if not generation_config.get("auto_generate_on_hook", False):
488
+ return
489
+
490
+ batch_size = generation_config.get("batch_size", 5)
491
+
492
+ # Only trigger if queue size meets batch threshold
493
+ if queue_size < batch_size:
494
+ return
495
+
496
+ # Spawn background generation process
497
+ _spawn_background_generate(repo_path)
498
+
499
+
500
+ def _spawn_background_generate(repo_path: Path) -> None:
501
+ """Spawn a background process to generate stories for a repo.
502
+
503
+ Uses subprocess.Popen with detached process to not block the git hook.
504
+ Logs to ~/.repr/logs/auto_generate.log
505
+
506
+ Args:
507
+ repo_path: Path to repository to generate stories for
508
+ """
509
+ import subprocess
510
+ import sys
511
+ from .config import REPR_HOME
512
+
513
+ # Ensure log directory exists
514
+ log_dir = REPR_HOME / "logs"
515
+ log_dir.mkdir(parents=True, exist_ok=True)
516
+ log_file = log_dir / "auto_generate.log"
517
+
518
+ # Build command - use sys.executable to find repr command
519
+ # repr generate --repo <path> --local --json
520
+ cmd = [
521
+ sys.executable, "-m", "repr",
522
+ "generate",
523
+ "--repo", str(repo_path),
524
+ "--local", # Always use local LLM for auto-generation
525
+ "--json",
526
+ ]
527
+
528
+ try:
529
+ # Open log file for appending
530
+ with open(log_file, "a") as log:
531
+ log.write(f"\n[{datetime.now().isoformat()}] Auto-generating for {repo_path}\n")
532
+ log.flush()
533
+
534
+ # Spawn detached background process
535
+ # On Unix, use start_new_session=True to fully detach
536
+ # On Windows, use DETACHED_PROCESS flag
537
+ if sys.platform == "win32":
538
+ DETACHED_PROCESS = 0x00000008
539
+ subprocess.Popen(
540
+ cmd,
541
+ stdout=log,
542
+ stderr=log,
543
+ creationflags=DETACHED_PROCESS,
544
+ close_fds=True,
545
+ )
546
+ else:
547
+ subprocess.Popen(
548
+ cmd,
549
+ stdout=log,
550
+ stderr=log,
551
+ start_new_session=True,
552
+ close_fds=True,
553
+ )
554
+ except Exception as e:
555
+ # Silently fail - don't block the git commit
556
+ try:
557
+ with open(log_file, "a") as log:
558
+ log.write(f"[{datetime.now().isoformat()}] Error spawning auto-generate: {e}\n")
559
+ except Exception:
560
+ pass
463
561
 
464
562
 
465
563
  def dequeue_commits(repo_path: Path, commit_shas: list[str]) -> int:
@@ -538,6 +538,8 @@ def get_effective_llm_mode() -> tuple[str, dict[str, Any]]:
538
538
 
539
539
 
540
540
 
541
+
542
+
541
543
 
542
544
 
543
545
 
@@ -365,6 +365,8 @@ def clear_audit_log() -> int:
365
365
 
366
366
 
367
367
 
368
+
369
+
368
370
 
369
371
 
370
372
 
@@ -311,6 +311,8 @@ def get_pending_events() -> list[dict[str, Any]]:
311
311
 
312
312
 
313
313
 
314
+
315
+
314
316
 
315
317
 
316
318
 
@@ -9,103 +9,107 @@ Provides different prompts for generating stories based on use case:
9
9
  """
10
10
 
11
11
  from typing import Any
12
+ from pydantic import BaseModel, Field
13
+
14
+
15
+ class StoryOutput(BaseModel):
16
+ """Structured output for a generated story."""
17
+ summary: str = Field(description="One-line technical summary of the work (max 120 chars, no fluff)")
18
+ content: str = Field(description="Full technical description in markdown")
12
19
 
13
20
 
14
21
  # Template definitions
15
22
  TEMPLATES = {
16
23
  "resume": {
17
24
  "name": "Resume",
18
- "description": "Professional accomplishment summaries for resumes and portfolios",
19
- "system_prompt": """You are helping a developer document their work accomplishments for a professional resume or portfolio.
20
-
21
- Focus on:
22
- - Quantifiable impact where possible (performance improvements, user metrics, etc.)
23
- - Technical complexity and problem-solving
24
- - Leadership and collaboration
25
- - Technologies and skills demonstrated
26
-
27
- Write in first person, using action verbs. Keep it concise and impactful.
28
- Format: 2-3 bullet points per accomplishment, each 1-2 sentences.""",
29
- "user_prompt_template": """Based on these commits, write professional accomplishment summaries:
25
+ "description": "Technical work log for resumes and portfolios",
26
+ "system_prompt": """Extract technical work from commits. Be direct and specific.
27
+
28
+ Output JSON with:
29
+ - summary: One line, max 120 chars. State what was done technically. No adjectives, no fluff.
30
+ Good: "Added JWT refresh token rotation with Redis session store"
31
+ Bad: "Enhanced authentication system with improved security"
32
+ - content: Markdown with technical details. What was built, how, what tech.
33
+
34
+ Rules:
35
+ - Name specific technologies, libraries, patterns
36
+ - Describe the implementation, not the benefit
37
+ - No marketing language (enhanced, streamlined, robust, seamless)
38
+ - No resume verbs (spearheaded, leveraged, drove)
39
+ - If there's a metric, include it. If not, don't invent one.""",
40
+ "user_prompt_template": """Repository: {repo_name}
30
41
 
31
- Repository: {repo_name}
32
42
  Commits:
33
43
  {commits_summary}
34
44
 
35
- Generate 1-3 accomplishment summaries suitable for a resume.""",
45
+ Output JSON with summary and content.""",
36
46
  },
37
47
 
38
48
  "changelog": {
39
49
  "name": "Changelog",
40
50
  "description": "Technical change documentation for release notes",
41
- "system_prompt": """You are writing technical changelog entries for a software project.
51
+ "system_prompt": """Extract changes from commits for a changelog. Be specific.
52
+
53
+ Output JSON with:
54
+ - summary: One line describing the main change (max 120 chars)
55
+ - content: Markdown changelog with categories (Added/Changed/Fixed/Removed)
56
+
57
+ Rules:
58
+ - List actual changes, not benefits
59
+ - Include file/module names when relevant
60
+ - No fluff words (improved, enhanced, better)""",
61
+ "user_prompt_template": """Repository: {repo_name}
42
62
 
43
- Focus on:
44
- - What changed (features, fixes, improvements)
45
- - Why it matters (user impact, developer experience)
46
- - Breaking changes or migration notes
47
- - Technical details relevant to other developers
48
-
49
- Use conventional changelog format with categories:
50
- - Added: New features
51
- - Changed: Changes to existing functionality
52
- - Fixed: Bug fixes
53
- - Removed: Removed features
54
- - Security: Security improvements""",
55
- "user_prompt_template": """Generate changelog entries from these commits:
56
-
57
- Repository: {repo_name}
58
63
  Commits:
59
64
  {commits_summary}
60
65
 
61
- Write changelog entries grouped by category.""",
66
+ Output JSON with summary and content.""",
62
67
  },
63
68
 
64
69
  "narrative": {
65
70
  "name": "Narrative",
66
- "description": "Storytelling format for blogs or case studies",
67
- "system_prompt": """You are helping a developer tell the story of their work in an engaging narrative format.
71
+ "description": "Technical narrative for blogs or case studies",
72
+ "system_prompt": """Write a technical narrative from commits.
73
+
74
+ Output JSON with:
75
+ - summary: One-line description of what was built (max 120 chars)
76
+ - content: Markdown narrative explaining the technical work
68
77
 
69
78
  Focus on:
70
- - The challenge or problem being solved
71
- - The approach and decision-making process
72
- - Obstacles encountered and how they were overcome
73
- - Results and lessons learned
79
+ - What problem was solved
80
+ - How it was implemented technically
81
+ - What decisions were made and why
74
82
 
75
- Write in a conversational, engaging tone suitable for a blog post or case study.
76
- Use present tense for engagement. Include technical details but make it accessible.""",
77
- "user_prompt_template": """Tell the story of this development work:
83
+ No marketing language. Write like you're explaining to another engineer.""",
84
+ "user_prompt_template": """Repository: {repo_name}
78
85
 
79
- Repository: {repo_name}
80
86
  Commits:
81
87
  {commits_summary}
82
88
 
83
- Write a narrative (2-3 paragraphs) that would work as a blog post section.""",
89
+ Output JSON with summary and content.""",
84
90
  },
85
91
 
86
92
  "interview": {
87
93
  "name": "Interview Prep",
88
- "description": "Behavioral interview preparation with STAR format",
89
- "system_prompt": """You are helping a developer prepare for behavioral interviews using the STAR method.
94
+ "description": "Technical interview preparation",
95
+ "system_prompt": """Extract technical work for interview prep.
90
96
 
91
- Format each accomplishment as:
92
- - Situation: Context and background
93
- - Task: What needed to be done
94
- - Action: What you did specifically
95
- - Result: The outcome and impact
97
+ Output JSON with:
98
+ - summary: One-line technical summary (max 120 chars)
99
+ - content: Markdown with situation/task/action/result format
96
100
 
97
101
  Focus on:
98
- - Technical decision-making
99
- - Problem-solving approach
100
- - Collaboration and communication
101
- - Quantifiable results""",
102
- "user_prompt_template": """Create interview-ready stories from these commits:
102
+ - Specific technical decisions made
103
+ - Problems encountered and solutions
104
+ - Technologies and patterns used
105
+
106
+ No resume language. Be specific about what you actually did.""",
107
+ "user_prompt_template": """Repository: {repo_name}
103
108
 
104
- Repository: {repo_name}
105
109
  Commits:
106
110
  {commits_summary}
107
111
 
108
- Generate 1-2 STAR-format stories for behavioral interviews.""",
112
+ Output JSON with summary and content.""",
109
113
  },
110
114
  }
111
115
 
@@ -177,6 +177,8 @@ def confirm(message: str, default: bool = False) -> bool:
177
177
 
178
178
 
179
179
 
180
+
181
+
180
182
 
181
183
 
182
184
 
@@ -277,6 +277,8 @@ def perform_update(force: bool = False) -> bool:
277
277
 
278
278
 
279
279
 
280
+
281
+
280
282
 
281
283
 
282
284
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repr-cli
3
- Version: 0.2.11
3
+ Version: 0.2.12
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
@@ -39,6 +39,8 @@ if __name__ == "__main__":
39
39
 
40
40
 
41
41
 
42
+
43
+
42
44
 
43
45
 
44
46
 
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