ripperdoc 0.2.9__py3-none-any.whl → 0.2.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. ripperdoc/__init__.py +1 -1
  2. ripperdoc/cli/cli.py +235 -14
  3. ripperdoc/cli/commands/__init__.py +2 -0
  4. ripperdoc/cli/commands/agents_cmd.py +132 -5
  5. ripperdoc/cli/commands/clear_cmd.py +8 -0
  6. ripperdoc/cli/commands/exit_cmd.py +1 -0
  7. ripperdoc/cli/commands/models_cmd.py +3 -3
  8. ripperdoc/cli/commands/resume_cmd.py +4 -0
  9. ripperdoc/cli/commands/stats_cmd.py +244 -0
  10. ripperdoc/cli/ui/panels.py +1 -0
  11. ripperdoc/cli/ui/rich_ui.py +295 -24
  12. ripperdoc/cli/ui/spinner.py +30 -18
  13. ripperdoc/cli/ui/thinking_spinner.py +1 -2
  14. ripperdoc/cli/ui/wizard.py +6 -8
  15. ripperdoc/core/agents.py +10 -3
  16. ripperdoc/core/config.py +3 -6
  17. ripperdoc/core/default_tools.py +90 -10
  18. ripperdoc/core/hooks/events.py +4 -0
  19. ripperdoc/core/hooks/llm_callback.py +59 -0
  20. ripperdoc/core/permissions.py +78 -4
  21. ripperdoc/core/providers/openai.py +29 -19
  22. ripperdoc/core/query.py +192 -31
  23. ripperdoc/core/tool.py +9 -4
  24. ripperdoc/sdk/client.py +77 -2
  25. ripperdoc/tools/background_shell.py +305 -134
  26. ripperdoc/tools/bash_tool.py +42 -13
  27. ripperdoc/tools/file_edit_tool.py +159 -50
  28. ripperdoc/tools/file_read_tool.py +20 -0
  29. ripperdoc/tools/file_write_tool.py +7 -8
  30. ripperdoc/tools/lsp_tool.py +615 -0
  31. ripperdoc/tools/task_tool.py +514 -65
  32. ripperdoc/utils/conversation_compaction.py +1 -1
  33. ripperdoc/utils/file_watch.py +206 -3
  34. ripperdoc/utils/lsp.py +806 -0
  35. ripperdoc/utils/message_formatting.py +5 -2
  36. ripperdoc/utils/messages.py +21 -1
  37. ripperdoc/utils/permissions/tool_permission_utils.py +174 -15
  38. ripperdoc/utils/session_heatmap.py +244 -0
  39. ripperdoc/utils/session_stats.py +293 -0
  40. {ripperdoc-0.2.9.dist-info → ripperdoc-0.2.10.dist-info}/METADATA +8 -2
  41. {ripperdoc-0.2.9.dist-info → ripperdoc-0.2.10.dist-info}/RECORD +45 -39
  42. {ripperdoc-0.2.9.dist-info → ripperdoc-0.2.10.dist-info}/WHEEL +0 -0
  43. {ripperdoc-0.2.9.dist-info → ripperdoc-0.2.10.dist-info}/entry_points.txt +0 -0
  44. {ripperdoc-0.2.9.dist-info → ripperdoc-0.2.10.dist-info}/licenses/LICENSE +0 -0
  45. {ripperdoc-0.2.9.dist-info → ripperdoc-0.2.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,293 @@
1
+ """Session statistics collection and analysis."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections import defaultdict
6
+ from dataclasses import dataclass, field
7
+ from datetime import datetime, timedelta
8
+ from pathlib import Path
9
+ from typing import Dict, List, Tuple
10
+
11
+ from ripperdoc.utils.log import get_logger
12
+ from ripperdoc.utils.session_history import list_session_summaries
13
+
14
+ logger = get_logger()
15
+
16
+
17
+ @dataclass
18
+ class SessionStats:
19
+ """Aggregated statistics across all sessions."""
20
+
21
+ # Basic counts
22
+ total_sessions: int = 0
23
+ total_messages: int = 0
24
+ total_cost_usd: float = 0.0
25
+
26
+ # Token statistics
27
+ total_tokens: int = 0
28
+ total_input_tokens: int = 0
29
+ total_output_tokens: int = 0
30
+ total_cache_read_tokens: int = 0
31
+ total_cache_creation_tokens: int = 0
32
+
33
+ # Model statistics (model -> count)
34
+ model_usage: Dict[str, int] = field(default_factory=dict)
35
+ favorite_model: str = ""
36
+
37
+ # Time statistics
38
+ longest_session_duration: timedelta = field(default_factory=lambda: timedelta(0))
39
+ earliest_session: datetime | None = None
40
+ latest_session: datetime | None = None
41
+
42
+ # Streak statistics
43
+ current_streak: int = 0
44
+ longest_streak: int = 0
45
+ active_days: int = 0
46
+ total_days: int = 0
47
+
48
+ # Activity patterns (hour -> count)
49
+ hourly_activity: Dict[int, int] = field(default_factory=dict)
50
+
51
+ # Daily activity (date_str -> count)
52
+ daily_activity: Dict[str, int] = field(default_factory=dict)
53
+
54
+ # Weekday activity (0=Monday -> 6=Sunday, count)
55
+ weekday_activity: Dict[int, int] = field(default_factory=dict)
56
+
57
+ # Peak hour (hour with most activity)
58
+ peak_hour: int = 0
59
+
60
+
61
+ def _calculate_streaks(active_dates: List[datetime]) -> Tuple[int, int]:
62
+ """Calculate current and longest streak from sorted active dates."""
63
+ if not active_dates:
64
+ return 0, 0
65
+
66
+ # Sort dates
67
+ sorted_dates = sorted(set(d.date() for d in active_dates))
68
+
69
+ # Calculate longest streak
70
+ longest = 1
71
+ current = 1
72
+ for i in range(1, len(sorted_dates)):
73
+ if (sorted_dates[i] - sorted_dates[i - 1]).days == 1:
74
+ current += 1
75
+ longest = max(longest, current)
76
+ else:
77
+ current = 1
78
+
79
+ # Calculate current streak (from today backwards)
80
+ today = datetime.now().date()
81
+ current_streak = 0
82
+
83
+ # Check if today or yesterday has activity
84
+ if sorted_dates[-1] == today:
85
+ current_streak = 1
86
+ check_date = today - timedelta(days=1)
87
+ elif sorted_dates[-1] == today - timedelta(days=1):
88
+ current_streak = 1
89
+ check_date = sorted_dates[-1] - timedelta(days=1)
90
+ else:
91
+ return 0, longest
92
+
93
+ # Count backwards
94
+ i = len(sorted_dates) - 2
95
+ while i >= 0:
96
+ if sorted_dates[i] == check_date:
97
+ current_streak += 1
98
+ check_date -= timedelta(days=1)
99
+ i -= 1
100
+ else:
101
+ break
102
+
103
+ return current_streak, longest
104
+
105
+
106
+ def collect_session_stats(project_path: Path, days: int = 32) -> SessionStats:
107
+ """Collect statistics from session history.
108
+
109
+ Args:
110
+ project_path: Project root directory
111
+ days: Number of days to look back (default 32)
112
+ """
113
+ stats = SessionStats(
114
+ hourly_activity=defaultdict(int),
115
+ daily_activity=defaultdict(int),
116
+ weekday_activity=defaultdict(int),
117
+ model_usage=defaultdict(int),
118
+ )
119
+
120
+ summaries = list_session_summaries(project_path)
121
+ if not summaries:
122
+ return stats
123
+
124
+ # Filter by date range (use timezone-aware cutoff if needed)
125
+ from datetime import timezone
126
+ cutoff = datetime.now(timezone.utc) - timedelta(days=days)
127
+
128
+ # Ensure comparison works with both naive and aware datetimes
129
+ recent_summaries = []
130
+ for s in summaries:
131
+ # Make updated_at timezone-aware if it's naive
132
+ updated_at = s.updated_at
133
+ if updated_at.tzinfo is None:
134
+ updated_at = updated_at.replace(tzinfo=timezone.utc)
135
+ if updated_at >= cutoff:
136
+ recent_summaries.append(s)
137
+
138
+ if not recent_summaries:
139
+ return stats
140
+
141
+ # Basic counts
142
+ stats.total_sessions = len(recent_summaries)
143
+ stats.total_messages = sum(s.message_count for s in recent_summaries)
144
+
145
+ # Time statistics
146
+ stats.earliest_session = min(s.created_at for s in recent_summaries)
147
+ stats.latest_session = max(s.updated_at for s in recent_summaries)
148
+ stats.total_days = (stats.latest_session - stats.earliest_session).days + 1
149
+
150
+ # Calculate longest session and activity patterns in single pass
151
+ active_dates: List[datetime] = []
152
+ date_set: set[str] = set()
153
+
154
+ for summary in recent_summaries:
155
+ # Longest session
156
+ duration = summary.updated_at - summary.created_at
157
+ if duration > stats.longest_session_duration:
158
+ stats.longest_session_duration = duration
159
+
160
+ # Track dates
161
+ date_str = summary.updated_at.date().isoformat()
162
+ if date_str not in date_set:
163
+ date_set.add(date_str)
164
+ active_dates.append(summary.updated_at)
165
+
166
+ # Hourly activity
167
+ stats.hourly_activity[summary.updated_at.hour] += 1
168
+
169
+ # Daily activity (for heatmap)
170
+ stats.daily_activity[date_str] += 1
171
+
172
+ # Weekday activity
173
+ stats.weekday_activity[summary.updated_at.weekday()] += 1
174
+
175
+ # Active days
176
+ stats.active_days = len(date_set)
177
+
178
+ # Streaks
179
+ stats.current_streak, stats.longest_streak = _calculate_streaks(active_dates)
180
+
181
+ # Peak hour
182
+ if stats.hourly_activity:
183
+ stats.peak_hour = max(stats.hourly_activity.items(), key=lambda x: x[1])[0]
184
+
185
+ # Load detailed session data for token and model statistics
186
+ import json
187
+
188
+ for summary in recent_summaries:
189
+ session_file = summary.path
190
+ if not session_file.exists():
191
+ continue
192
+
193
+ try:
194
+ with session_file.open("r", encoding="utf-8") as fh:
195
+ for line in fh:
196
+ if not line.strip():
197
+ continue
198
+ # Quick string check before full JSON parse
199
+ if '"type":"assistant"' not in line and '"type": "assistant"' not in line:
200
+ continue
201
+ try:
202
+ entry = json.loads(line)
203
+ payload = entry.get("payload", {})
204
+
205
+ # Only process assistant messages
206
+ if payload.get("type") != "assistant":
207
+ continue
208
+
209
+ # Extract model and token information
210
+ model = payload.get("model")
211
+ if model:
212
+ stats.model_usage[model] += 1
213
+
214
+ # Extract token counts
215
+ input_tokens = payload.get("input_tokens", 0)
216
+ output_tokens = payload.get("output_tokens", 0)
217
+ cache_read = payload.get("cache_read_tokens", 0)
218
+ cache_creation = payload.get("cache_creation_tokens", 0)
219
+
220
+ stats.total_input_tokens += input_tokens
221
+ stats.total_output_tokens += output_tokens
222
+ stats.total_cache_read_tokens += cache_read
223
+ stats.total_cache_creation_tokens += cache_creation
224
+
225
+ # Extract cost
226
+ cost = payload.get("cost_usd", 0.0)
227
+ stats.total_cost_usd += cost
228
+
229
+ except (json.JSONDecodeError, KeyError, TypeError, ValueError):
230
+ continue
231
+ except (OSError, IOError):
232
+ continue
233
+
234
+ # Calculate total tokens
235
+ stats.total_tokens = (
236
+ stats.total_input_tokens
237
+ + stats.total_output_tokens
238
+ + stats.total_cache_read_tokens
239
+ + stats.total_cache_creation_tokens
240
+ )
241
+
242
+ # Determine favorite model
243
+ if stats.model_usage:
244
+ stats.favorite_model = max(stats.model_usage.items(), key=lambda x: x[1])[0]
245
+
246
+ return stats
247
+
248
+
249
+ def format_duration(td: timedelta) -> str:
250
+ """Format timedelta as human-readable string."""
251
+ total_seconds = int(td.total_seconds())
252
+ if total_seconds < 60:
253
+ return f"{total_seconds}s"
254
+
255
+ minutes = total_seconds // 60
256
+ if minutes < 60:
257
+ seconds = total_seconds % 60
258
+ return f"{minutes}m {seconds}s"
259
+
260
+ hours = minutes // 60
261
+ remaining_mins = minutes % 60
262
+ if hours < 24:
263
+ return f"{hours}h {remaining_mins}m"
264
+
265
+ days = hours // 24
266
+ remaining_hours = hours % 24
267
+ return f"{days}d {remaining_hours}h {remaining_mins}m"
268
+
269
+
270
+ def format_large_number(num: int) -> str:
271
+ """Format large numbers with k/m/b suffix.
272
+
273
+ Examples:
274
+ 1234 -> "1.2k"
275
+ 1234567 -> "1.2m"
276
+ 1234567890 -> "1.2b"
277
+ """
278
+ if num < 1000:
279
+ return str(num)
280
+ elif num < 1_000_000:
281
+ return f"{num / 1000:.1f}k"
282
+ elif num < 1_000_000_000:
283
+ return f"{num / 1_000_000:.1f}m"
284
+ else:
285
+ return f"{num / 1_000_000_000:.1f}b"
286
+
287
+
288
+ __all__ = [
289
+ "SessionStats",
290
+ "collect_session_stats",
291
+ "format_duration",
292
+ "format_large_number",
293
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripperdoc
3
- Version: 0.2.9
3
+ Version: 0.2.10
4
4
  Summary: AI-powered terminal assistant for coding tasks
5
5
  Author: Ripperdoc Team
6
6
  License: Apache-2.0
@@ -33,6 +33,12 @@ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
33
33
  Requires-Dist: mypy>=1.0.0; extra == "dev"
34
34
  Requires-Dist: black>=23.0.0; extra == "dev"
35
35
  Requires-Dist: ruff>=0.1.0; extra == "dev"
36
+ Provides-Extra: anthropic
37
+ Requires-Dist: anthropic>=0.39.0; extra == "anthropic"
38
+ Provides-Extra: openai
39
+ Requires-Dist: openai>=1.0.0; extra == "openai"
40
+ Provides-Extra: gemini
41
+ Requires-Dist: google-genai>=0.3.0; extra == "gemini"
36
42
  Dynamic: license-file
37
43
 
38
44
  <div align="center">
@@ -137,7 +143,7 @@ See the [examples/](examples/) directory for complete SDK usage examples.
137
143
 
138
144
  ### Safe Mode Permissions
139
145
 
140
- Safe mode is the default. Use `--unsafe` to skip permission prompts. Choose `a`/`always` to allow a tool for the current session (not persisted across sessions).
146
+ Safe mode is the default. Use `--yolo` to skip permission prompts. Choose `a`/`always` to allow a tool for the current session (not persisted across sessions).
141
147
 
142
148
  ### Agent Skills
143
149
 
@@ -1,24 +1,25 @@
1
- ripperdoc/__init__.py,sha256=byWZLSwiJLpDsuaMe8M0HKTrEKcgffzCd6PGUkDvk7c,66
1
+ ripperdoc/__init__.py,sha256=oGsHv3v5Rn-xgN3ZJ5m1ExqzAZleylUN7J1e5kmk5bA,67
2
2
  ripperdoc/__main__.py,sha256=1Avq2MceBfwUlNsfasC8n4dqVL_V56Bl3DRsnY4_Nxk,370
3
3
  ripperdoc/cli/__init__.py,sha256=03wf6gXBcEgXJrDJS-W_5BEG_DdJ_ep7CxQFPML-73g,35
4
- ripperdoc/cli/cli.py,sha256=s-jR23j7stf-BleLvGJRBYAWqPESdf4fcGj_g4HVKus,11381
5
- ripperdoc/cli/commands/__init__.py,sha256=pINc_mUWj1D3bXrR6lzhWAAJFAyUTlDWba8OJpZIiXs,4651
6
- ripperdoc/cli/commands/agents_cmd.py,sha256=3kVebe7F3Uo0GWVGELt8xnv9CRdjDiXSPANGAospHXQ,10486
4
+ ripperdoc/cli/cli.py,sha256=BHZTyn5AfVOUE5-dmj6hEdkXgMDJ0YB65wYDY3eAlBM,19207
5
+ ripperdoc/cli/commands/__init__.py,sha256=Z8REiBCaBjGT8Ce4gD3h5MH3YyhTM9mVqhaaU85E7WQ,4718
6
+ ripperdoc/cli/commands/agents_cmd.py,sha256=AlkuSxdIsAC53BhGz5kztfgNmjoaOHs4kHrVwEzI22I,15763
7
7
  ripperdoc/cli/commands/base.py,sha256=4KUjxCM04MwbSMUKVNEBph_jeAKPI8b5MHsUFoz7l5g,386
8
- ripperdoc/cli/commands/clear_cmd.py,sha256=Ahfbse0-OPG99fcSaQK9NLNxPDHtvGirB1vFWhFTDUY,354
8
+ ripperdoc/cli/commands/clear_cmd.py,sha256=iyOLWtgYwJKNjG-el2mwFRA3VWGIXrNNLl32Xdwpq9o,584
9
9
  ripperdoc/cli/commands/compact_cmd.py,sha256=uR_nB1OX7cUL1TOJoefwdO31Qfyjd0nZSSttErqUxbA,473
10
10
  ripperdoc/cli/commands/config_cmd.py,sha256=QcFYOOmNFSHmw6K2vY_wfKrYXi8PSzz6koJFREJoF_c,884
11
11
  ripperdoc/cli/commands/context_cmd.py,sha256=tM8o2ZfX-axFYaFLsWOTSER_Yevk3ANr2numHfuh2UE,5232
12
12
  ripperdoc/cli/commands/cost_cmd.py,sha256=yD9LSqgxVvYNTDPnEHxugjyLWcmbtH5dXim7DIW9zXc,2822
13
13
  ripperdoc/cli/commands/doctor_cmd.py,sha256=K3bea4DHb34twx_lr_iyPpxdTGvmx4OewGGfKpXSM-Y,7170
14
- ripperdoc/cli/commands/exit_cmd.py,sha256=B0CNKQos2eRC4LSjizLdKsFYzFfwRkrUur6Afu3Fh9M,334
14
+ ripperdoc/cli/commands/exit_cmd.py,sha256=lEGLMVozoOM2Ea_Yw-sbaIvAbfb_Nx3UTpC49ga-MZ8,376
15
15
  ripperdoc/cli/commands/help_cmd.py,sha256=jyK6U2bsGEIwFpu08slVHKfxRyS3oblnRXdqSgU_W4w,978
16
16
  ripperdoc/cli/commands/hooks_cmd.py,sha256=-ixQhKb1CX2c7_2zDdAqXV9ThnMf31UH3a9UBERD9mw,21026
17
17
  ripperdoc/cli/commands/mcp_cmd.py,sha256=ZCnswx0TIiaiUUsIX7NpHaLZLZtvlUhBnN12s_ZtPCA,2424
18
18
  ripperdoc/cli/commands/memory_cmd.py,sha256=gDvRr_-U1gMrOdC3OvujYLL5_CUgyZpwaJdytRP5CBM,6549
19
- ripperdoc/cli/commands/models_cmd.py,sha256=ZuFekqIgO56XYrOWM3my4PyP6Q4YwO05HyZ0eENNL4I,16424
19
+ ripperdoc/cli/commands/models_cmd.py,sha256=Jjj7iw4p5ydBZ8RBl4ozdgBozlKWFyXwF7FWzSKjHOo,16373
20
20
  ripperdoc/cli/commands/permissions_cmd.py,sha256=aIMIvmt78nB2Q-Qa5ojqClUOsPRcoyeDIuhOJX8xg2k,11559
21
- ripperdoc/cli/commands/resume_cmd.py,sha256=QQL3tQgv5Ae2SRyt4KoCuSPBKR5Ws3LFj-KhcssEtT0,4151
21
+ ripperdoc/cli/commands/resume_cmd.py,sha256=zcZ-pimjn-XPm5s3dw-SCkw1-ALVFlFUzT1zmkTH9Cg,4268
22
+ ripperdoc/cli/commands/stats_cmd.py,sha256=W0KKp4qTyo9olDS_AiJi3tri7m0W37pjX16Nd4g0p0c,7855
22
23
  ripperdoc/cli/commands/status_cmd.py,sha256=Sqm8-5Q-3gui2relPHA99z2R50ft1mkf2QnqvKlDtVM,5535
23
24
  ripperdoc/cli/commands/tasks_cmd.py,sha256=M-LuowiRt4xSzvIv_m5-h9KTi5o3KvdV9k2VQBqyuYM,8867
24
25
  ripperdoc/cli/commands/todos_cmd.py,sha256=7Q0B1NVqGtB3R29ndbn4m0VQQm-YQ7d4Wlk7vJ7dLQI,1848
@@ -29,58 +30,60 @@ ripperdoc/cli/ui/file_mention_completer.py,sha256=PYfcauxhU4NkLsNP-fGeBhZQFzUeKo
29
30
  ripperdoc/cli/ui/helpers.py,sha256=DmgMMouyQdesjQ5RsErwsRCKVdWiDJnpqJjv90a3neE,2545
30
31
  ripperdoc/cli/ui/interrupt_handler.py,sha256=mZAZhBXID6_NR03K8fSjPDDjtTScy182VFBbVHamcoM,5914
31
32
  ripperdoc/cli/ui/message_display.py,sha256=W9VlRCpMykkamxphej5LHgmU-P4YaBfm9HPdIKlFKpg,10573
32
- ripperdoc/cli/ui/panels.py,sha256=lzgg7kP8nzJKqGFjE-0UVbr9a1YZ0i2XlkUy6-LcLnk,1875
33
+ ripperdoc/cli/ui/panels.py,sha256=A0xX0Lxk7WTSz5y8sJDAYnpFFAX5WWql0199teuTipA,1929
33
34
  ripperdoc/cli/ui/provider_options.py,sha256=Ic30K1ev8w2oEcha4IjDYSoxw1tyCVB4hLoQpS_Y_5E,8369
34
- ripperdoc/cli/ui/rich_ui.py,sha256=Ep884Vgvui64I3QRzwR8tUqB9iQGFnOpyup_nIEDMhY,48864
35
- ripperdoc/cli/ui/spinner.py,sha256=Qavm98n5CEGKA01pgSSjHiFkqd3PgzoZdZAm7vYHrgk,2092
36
- ripperdoc/cli/ui/thinking_spinner.py,sha256=9Et5EqPChfkmkiOO8w1OPs8t-sHaisgjn9A__kEYLyg,2824
35
+ ripperdoc/cli/ui/rich_ui.py,sha256=PHEl0epQyeOGc9qomjjUsSmfrXPHPEroGDgLnljB5Tw,60789
36
+ ripperdoc/cli/ui/spinner.py,sha256=4T47qbT_YZlFkzWMGew_NP7fg_xf81AkWbWo8gJskZM,2663
37
+ ripperdoc/cli/ui/thinking_spinner.py,sha256=3zmxj3vd-1njdiHF2Afsm7RGiRl7645AEc7fTLKgAbU,2805
37
38
  ripperdoc/cli/ui/tool_renderers.py,sha256=tWXFyHOHz-kryoidra5RL-h9K8nbjrTBZ9o_thIgRho,11220
38
- ripperdoc/cli/ui/wizard.py,sha256=L__1kIbySlyUkd4RKreOLkKC12z9O9vE9ZJN_QfLyDg,7055
39
+ ripperdoc/cli/ui/wizard.py,sha256=vc_uGuuf6F73bDB4DSgfTki8kVzwMywQ_u9YyGPuWPY,6972
39
40
  ripperdoc/core/__init__.py,sha256=UemJCA-Y8df1466AX-YbRFj071zKajmqO1mi40YVW2g,40
40
- ripperdoc/core/agents.py,sha256=cGRdUeOiYUeJbZqxxURa6drXHOuMrmyt2Dwgl_nAhjc,20045
41
+ ripperdoc/core/agents.py,sha256=t7Bc5BqUlSp8Xzqe2lMGhM9oupEJIsUBNsUaJGkwt9Y,20343
41
42
  ripperdoc/core/commands.py,sha256=NXCkljYbAP4dDoRy-_3semFNWxG4YAk9q82u8FTKH60,835
42
- ripperdoc/core/config.py,sha256=ZzLlAqcQ-_RqcxdIGDEaYiLwowpT3gTbKAFOHWQx6Uk,22059
43
+ ripperdoc/core/config.py,sha256=hjC6c02HGSzwJ0pEu2Gp0w0X_6yiZ_UwxFG2H-u1FC4,21947
43
44
  ripperdoc/core/custom_commands.py,sha256=2BMYiBq15FDjl3aOa3styN6nARyfU7xFAb4ly2Vsp-w,14254
44
- ripperdoc/core/default_tools.py,sha256=6IZjEo56b8nyW-0I6ye9C3svoK6Tjnlc4gB5eL_PILM,3165
45
- ripperdoc/core/permissions.py,sha256=4cwPiPVTXVOMfrl9WxO1akAcLiHr9aWmhCZtfrrgB-c,11026
46
- ripperdoc/core/query.py,sha256=9gCIXt8mlVFtA0rzkWaYqC-8nFhfU0OTlWfg5lQVNPg,42726
45
+ ripperdoc/core/default_tools.py,sha256=TWNo537SqI4ViWqINpbR-SOiBe40Chl2l0A1ddaUVqY,5249
46
+ ripperdoc/core/permissions.py,sha256=v8pFdLw3fYr73ynW2pkANXH34M6Djf3iDcM4KfLOS7I,13901
47
+ ripperdoc/core/query.py,sha256=WXAgwQbozeeRi7kZGGQYoZc1peBWW3kAQpzGoJqBoe0,50298
47
48
  ripperdoc/core/query_utils.py,sha256=xF9_vcqVSKANd9gwbk7aIG0t4a_f6R0m78-p3j5vkyI,24923
48
49
  ripperdoc/core/skills.py,sha256=cco9vpQxg_5HCKp64k6TtN01NaWbvWef2A9olbpTZaU,10366
49
50
  ripperdoc/core/system_prompt.py,sha256=d3GNCsJ_mdIojpWg1Wc0gRDC_gzRDI5G4tQN_gxhRdo,26752
50
- ripperdoc/core/tool.py,sha256=7CUAEcoztTm211GCFiv3wYEb-rbIKut_tA92Oz5ueNo,7855
51
+ ripperdoc/core/tool.py,sha256=m9O8MvzCS_8q1QvwO7-ZlYepyly7egjHjMIzgmz6eeA,8124
51
52
  ripperdoc/core/hooks/__init__.py,sha256=xw7VJQu1ZB0ENHVqL5xtruBnP3d0FNgrBH6NTL2xYgg,2735
52
53
  ripperdoc/core/hooks/config.py,sha256=wE_eMHDZu9-yHGyJ45ySL1_l2yx7B8i4jiThs78W6Zc,10085
53
- ripperdoc/core/hooks/events.py,sha256=bgk0Ie06ZmKdmAx_NeLkMOQj556LtfC-xDLPMiBQAQ0,18168
54
+ ripperdoc/core/hooks/events.py,sha256=UGBDdec52abi2e6ox8ncsLeDo3t9ZJesymKaPwxNDAE,18403
54
55
  ripperdoc/core/hooks/executor.py,sha256=3MdiCdc4Bn2xHcTlRT72BajrgAZ5roKck0NBnO4Re0c,16781
55
56
  ripperdoc/core/hooks/integration.py,sha256=IzkOSpaMjC397zKdKO1jTR0uyzOet-eCwPLuXwTYOts,11082
57
+ ripperdoc/core/hooks/llm_callback.py,sha256=umUB0iG9BcHv8PQvBflrM-LIkqxoc9e1pl5OfkyXi80,1734
56
58
  ripperdoc/core/hooks/manager.py,sha256=11CVV3GHbVETNCw5hYG_Fn-CgSHP-PncBdmuqlvKso8,24607
57
59
  ripperdoc/core/providers/__init__.py,sha256=yevsHF0AUI4b6Wiq_401NXewJ3dqe8LUUtQm0TLPPNQ,1911
58
60
  ripperdoc/core/providers/anthropic.py,sha256=B967szN1Thc0K1Iv8TvKWcVKktCevBmShftupVag8pk,28551
59
61
  ripperdoc/core/providers/base.py,sha256=HNOa3_XWszu6DbI8BYixxV0hnZb9qZ_FU4uinFVRHjU,9271
60
62
  ripperdoc/core/providers/gemini.py,sha256=Fs-dShsmIVBFfz-jg4fBjvQyrxVnZ5yx4ALcES-l5Sg,27089
61
- ripperdoc/core/providers/openai.py,sha256=DKt4VLiLBwhrqOkKBjafI4Eq-eIwSHJPnaMzMuse9yQ,23126
63
+ ripperdoc/core/providers/openai.py,sha256=DukxAXZJUVIZPDEuFwG748y3iucSIJnPQUqGjX_qUgQ,23675
62
64
  ripperdoc/sdk/__init__.py,sha256=aDSgI4lcCDs9cV3bxNmEEV3SuYx2aCd4VnUjs6H-R7E,213
63
- ripperdoc/sdk/client.py,sha256=yRa5QRTqax5ko0mQBsh0UCo74VqYT0SxjWV73UmuQUQ,11357
65
+ ripperdoc/sdk/client.py,sha256=ioDp1xwTUPmdTzBemKjZroO9bu6RC8geJk5Io9AkkHE,14785
64
66
  ripperdoc/tools/__init__.py,sha256=RBFz0DDnztDXMqv_zRxFHVY-ez2HYcncx8zh_y-BX6w,42
65
67
  ripperdoc/tools/ask_user_question_tool.py,sha256=ZWg5xAdeaRoR98KvvPuKmJH4L2dgzH87VXM4dxKcqBE,15478
66
- ripperdoc/tools/background_shell.py,sha256=yPwh1zvQVU4uk-lXqg0fL4JJyY-s96sA4qpYK9jlRT8,12866
68
+ ripperdoc/tools/background_shell.py,sha256=qrLxxwK85KsZTrQpw_bdAfz2EE4tSdut8u0xXbEXXQg,19525
67
69
  ripperdoc/tools/bash_output_tool.py,sha256=cC5dDmKYmkOTsLCXCcTYgc0loVWtmRobPn0C-I6qO-o,3379
68
- ripperdoc/tools/bash_tool.py,sha256=EMXfA3WXCveOyx-XJU19oG_lz-HwBY_Y3QmR_xSIxCU,43211
70
+ ripperdoc/tools/bash_tool.py,sha256=jKPx_dTcN7npULsNCDfP50xsgxus4yDo88JKnOvEbtU,44644
69
71
  ripperdoc/tools/dynamic_mcp_tool.py,sha256=kxxWhp6pP9N8fK3ubu5fHQYQv7aSwxcnaa3C9ZsBbOU,15879
70
72
  ripperdoc/tools/enter_plan_mode_tool.py,sha256=LNQv43uWkohiQTYQdsrKbpAfQSJNE_FJ9Y6AM_ckjng,7976
71
73
  ripperdoc/tools/exit_plan_mode_tool.py,sha256=XxhEih5EUrcscvkRA9lel54XoVIhl_cCL2xcvMJjtVA,5756
72
- ripperdoc/tools/file_edit_tool.py,sha256=8o31c2QiFVXyxjyBnFHsXc8XZjkh2_j3tWP6U2WpM0Q,13826
73
- ripperdoc/tools/file_read_tool.py,sha256=TGhb91dzQFwKUlRIANybfSBXrkQq_MAeazJmYB_Py60,7454
74
- ripperdoc/tools/file_write_tool.py,sha256=g-fJWqoIdOjMqpNBcTAkRaMtC502QGnG5Eu-qE3nF20,7106
74
+ ripperdoc/tools/file_edit_tool.py,sha256=lk54kJo7KbwPMRLwcLnLpptt7gx3Y2LXj-r5bKnD1pQ,19172
75
+ ripperdoc/tools/file_read_tool.py,sha256=jB4g0jF2Bl5WCzZNrLLTfzM_FmLDGTxK2q3eetrBkhw,8553
76
+ ripperdoc/tools/file_write_tool.py,sha256=J6SCapkBGN0Wotfb8ce0sKhAtyC7SloKNSDqrBlkdcU,7040
75
77
  ripperdoc/tools/glob_tool.py,sha256=eZG4fzahjJsSM8NdmTiVl5nBfDQK7egPg6P7cqOM_1Y,5948
76
78
  ripperdoc/tools/grep_tool.py,sha256=ng2vJjMnkhY9ZukKrKTYLAHI0ZqzmooEuaNe6gdocr0,14141
77
79
  ripperdoc/tools/kill_bash_tool.py,sha256=_jwnJVCPe8uXZTJd7myh4hWCD-eBuB2XDaYwRCmNSsI,4625
78
80
  ripperdoc/tools/ls_tool.py,sha256=JWQucgNOLjrGZwM7imu3GWe5YFwXxdXGoaBr50wDZCQ,15367
81
+ ripperdoc/tools/lsp_tool.py,sha256=dW7i0b0ziPy0W-Hnaj-T96EKovzx9guGZDoZY4dEeD0,21363
79
82
  ripperdoc/tools/mcp_tools.py,sha256=BVz8MJhijNnHq1J2LLkZS89wp_pwwnOytH1HYcfkFqQ,23085
80
83
  ripperdoc/tools/multi_edit_tool.py,sha256=Kq8kQ-j-kUgJWpx2OMSUjbjiFoiSaBLaYdg0avoZM0k,17607
81
84
  ripperdoc/tools/notebook_edit_tool.py,sha256=Y2XkQB4WDbSWeCsO3Ybnsrbcxki99aOv3o23jZ5D1Hw,14445
82
85
  ripperdoc/tools/skill_tool.py,sha256=TIUiUzL69mrKXFTais2YOssAwWnS5dwfK2_JFMG4xsY,7701
83
- ripperdoc/tools/task_tool.py,sha256=UVfhfebsehFVijp30agP3UwhnE2ImY6FKzbjto3hpLM,18345
86
+ ripperdoc/tools/task_tool.py,sha256=buQrpTeK-za9QBxePwa1tnZDTdKKRSwW2tyL7y4hCG8,32492
84
87
  ripperdoc/tools/todo_tool.py,sha256=eIwF-s1117DrXJ4yXUMwkNs1gfKYNF2OlWzkJAXDzmk,20001
85
88
  ripperdoc/tools/tool_search_tool.py,sha256=AeY-tFtWr2IAHTCEnG9kvsRRBqrBd-PJ96oUnFKa3Vs,13890
86
89
  ripperdoc/utils/__init__.py,sha256=gdso60znB2hsYZ_YZBKVcuOY3QVfoqD2wHQ4pvr5lSw,37
@@ -88,24 +91,27 @@ ripperdoc/utils/bash_constants.py,sha256=KNn8bzB6nVU5jid9jvjiH4FAu8pP3DZONJ-OknJ
88
91
  ripperdoc/utils/bash_output_utils.py,sha256=3Cf5wKJzRbUesmCNy5HtXIBtv0Z2BxklTfFHJ9q1T3w,1210
89
92
  ripperdoc/utils/coerce.py,sha256=KOPb4KR4p32nwHWG_6GsGHeVZunJyYc2YhC5DLmEZO8,1015
90
93
  ripperdoc/utils/context_length_errors.py,sha256=oyDVr_ME_6j97TLwVZ8bDMb6ISGQx6wEHrY7ckc0GuA,7714
91
- ripperdoc/utils/conversation_compaction.py,sha256=GcRJat9i3IAGWerEAdCJzVryiFxQ-3_Q5oXvvMTXZsE,18344
94
+ ripperdoc/utils/conversation_compaction.py,sha256=mbOYwQXXsAqiJ7mBQ25195bXtH3tb5GSU0HUNsvnl1g,18345
92
95
  ripperdoc/utils/exit_code_handlers.py,sha256=QtO1iDxVAb8Xp03D6_QixPoJC-RQlcp3ssIo_rm4two,7973
93
- ripperdoc/utils/file_watch.py,sha256=TxdVLNfbOHPb4Lv4IDfjOYjVxyNx1lbFQSofGVlO4sU,4114
96
+ ripperdoc/utils/file_watch.py,sha256=8uOPZNqiJffEPRnbBDJ-JwyVIMEPletBZIHfEHOmDuc,11653
94
97
  ripperdoc/utils/git_utils.py,sha256=Hq-Zx-KPyX4lp_i8ozhic15LyYdX_IfCRm-EyoFu59A,9047
95
98
  ripperdoc/utils/json_utils.py,sha256=Bx1pHHu5r7GtvCqFHM3K9EoknFAtYOaCqTn9RN-5qBA,757
96
99
  ripperdoc/utils/log.py,sha256=mieFMPxiv-M87bB-dgiY8D5WMxQbjVKJdsrW8QvCui8,6138
100
+ ripperdoc/utils/lsp.py,sha256=L0y9laUbj4gxR1rHgFOcloVGsH1QTPbv7eAmPsr2V64,26712
97
101
  ripperdoc/utils/mcp.py,sha256=92PMrGOzAWoqKGIiWBX2nNO-wkyQ-lgchmdlQ8Oo3zs,19548
98
102
  ripperdoc/utils/memory.py,sha256=J_kucw1BBnHQ1qG2_ZzdNysKvS1lrpuMtB5wxJDmXZU,8033
99
103
  ripperdoc/utils/message_compaction.py,sha256=FwYxjWc0B7IlzT1VPEyfhwEl8rXbDi37utbEbS5qGWw,22577
100
- ripperdoc/utils/message_formatting.py,sha256=Yye9TW1JyIKMgjtgn8f8HwM6GeYMTYUL4uxSOX9YLIY,7728
101
- ripperdoc/utils/messages.py,sha256=eUNA8Av1kf_tVrArkUn0nxHm2OfsBZ-NLGOBH2dTsxw,23771
104
+ ripperdoc/utils/message_formatting.py,sha256=0d0hDAUy-9G2Kxl16z63sPtTGAKLqtEZAhCeQCtmHBc,7828
105
+ ripperdoc/utils/messages.py,sha256=RP5jvZejiMCPhq6hyuyq1igBtYKRf658ZhbtGacTFfU,24340
102
106
  ripperdoc/utils/output_utils.py,sha256=R3wqFh9Dko_GK00Exx7XI0DnnldRWMsxZypYX5y6SJo,7448
103
107
  ripperdoc/utils/path_ignore.py,sha256=5VOk075Ef9Wz9LhAWjFQuXJfnypxTD0w9cZVoTGko2M,17882
104
108
  ripperdoc/utils/path_utils.py,sha256=C45Q3OeXnj-0FVEtvf_tdG5922XB6HthUzlUCvfc17Y,1626
105
109
  ripperdoc/utils/prompt.py,sha256=zICNEsA_OtKx8t3zo9tHLXXu6G5K8rPO3jFLKz4j5tg,560
106
110
  ripperdoc/utils/safe_get_cwd.py,sha256=lYxFJAN7lomoLwTAfMZtyOueotuvhC8TN84NtrPKj1E,827
107
111
  ripperdoc/utils/sandbox_utils.py,sha256=G91P8dw2VFcCiCpjXZ4LvzbAPiO8REqMhw39eI5Z4dU,1123
112
+ ripperdoc/utils/session_heatmap.py,sha256=5zEJCfAo6eP1byuvBjho0ojbhXP-u63bTcIfvHgbgKY,8442
108
113
  ripperdoc/utils/session_history.py,sha256=67RKV6eiX16FYYRR851MBFjgezkyP95LnllNZazWwcU,9704
114
+ ripperdoc/utils/session_stats.py,sha256=jWqkTGH5qplVW8aHOBy3JCWiKl70SvXroyr_fT7khb4,9100
109
115
  ripperdoc/utils/session_usage.py,sha256=p8_s46zDTzV1qzP4HR4PuZmLeJvSfq9mG_Y5rCnRYyA,3213
110
116
  ripperdoc/utils/shell_token_utils.py,sha256=SduoSU-RERJdM_7gBn0urr5UXtl4XOpPgydBd2fwzWg,2500
111
117
  ripperdoc/utils/shell_utils.py,sha256=t-neFPy_VhEmWZ79J7hh1ULBEdX116Rb9_pnXvir1Jw,5235
@@ -114,10 +120,10 @@ ripperdoc/utils/token_estimation.py,sha256=qPQbeUwVlafEjzsXw6qMo0hd4Vjb1gCUMAPBo
114
120
  ripperdoc/utils/permissions/__init__.py,sha256=33FfOaDLepxJSkp0RLvTdVu7qBXuEcnOoTHFbEtFOt0,653
115
121
  ripperdoc/utils/permissions/path_validation_utils.py,sha256=KOegjWaph8tXU7aqwQXRAxFEzrmRuPvdLb36J1QIPDQ,5772
116
122
  ripperdoc/utils/permissions/shell_command_validation.py,sha256=tEp0sJoijYdy0stLdYxekCxGlGhPTTd46lRrWaY7w-I,33280
117
- ripperdoc/utils/permissions/tool_permission_utils.py,sha256=6Fdu9-dMKhLsUExjEjoS0EUeRpEVN5UkqyseIC05YmM,9207
118
- ripperdoc-0.2.9.dist-info/licenses/LICENSE,sha256=bRv9UhBor6GhnQDj12RciDcRfu0R7sB-lqCy1sWF75c,9242
119
- ripperdoc-0.2.9.dist-info/METADATA,sha256=iGPG4ABTUQcIYplaFNlYh9-tMDY9RQDucfxnipJyjtM,7474
120
- ripperdoc-0.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
121
- ripperdoc-0.2.9.dist-info/entry_points.txt,sha256=79aohFxFPJmrQ3-Mhain04vb3EWpuc0EyzvDDUnwAu4,81
122
- ripperdoc-0.2.9.dist-info/top_level.txt,sha256=u8LbdTr1a-laHgCO0Utl_R3QGFUhLxWelCDnP2ZgpCU,10
123
- ripperdoc-0.2.9.dist-info/RECORD,,
123
+ ripperdoc/utils/permissions/tool_permission_utils.py,sha256=auKylTR73EVmkeQ5TTRRhCDMGl-cEEt882Qt6Fa5VSI,14579
124
+ ripperdoc-0.2.10.dist-info/licenses/LICENSE,sha256=bRv9UhBor6GhnQDj12RciDcRfu0R7sB-lqCy1sWF75c,9242
125
+ ripperdoc-0.2.10.dist-info/METADATA,sha256=GQ1s_0BaysD_QxGX9konIIMUKFY6T4-WstY_2fbHdjw,7702
126
+ ripperdoc-0.2.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
127
+ ripperdoc-0.2.10.dist-info/entry_points.txt,sha256=79aohFxFPJmrQ3-Mhain04vb3EWpuc0EyzvDDUnwAu4,81
128
+ ripperdoc-0.2.10.dist-info/top_level.txt,sha256=u8LbdTr1a-laHgCO0Utl_R3QGFUhLxWelCDnP2ZgpCU,10
129
+ ripperdoc-0.2.10.dist-info/RECORD,,