ripperdoc 0.2.7__py3-none-any.whl → 0.2.8__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.
ripperdoc/core/query.py CHANGED
@@ -29,6 +29,7 @@ from pydantic import ValidationError
29
29
  from ripperdoc.core.config import provider_protocol
30
30
  from ripperdoc.core.providers import ProviderClient, get_provider_client
31
31
  from ripperdoc.core.permissions import PermissionResult
32
+ from ripperdoc.core.hooks.manager import hook_manager
32
33
  from ripperdoc.core.query_utils import (
33
34
  build_full_system_prompt,
34
35
  determine_tool_mode,
@@ -154,6 +155,52 @@ async def _run_tool_use_generator(
154
155
  tool_context: ToolUseContext,
155
156
  ) -> AsyncGenerator[Union[UserMessage, ProgressMessage], None]:
156
157
  """Execute a single tool_use and yield progress/results."""
158
+ # Get tool input as dict for hooks
159
+ tool_input_dict = (
160
+ parsed_input.model_dump()
161
+ if hasattr(parsed_input, "model_dump")
162
+ else dict(parsed_input) if isinstance(parsed_input, dict) else {}
163
+ )
164
+
165
+ # Run PreToolUse hooks
166
+ pre_result = await hook_manager.run_pre_tool_use_async(
167
+ tool_name, tool_input_dict, tool_use_id=tool_use_id
168
+ )
169
+ if pre_result.should_block:
170
+ block_reason = pre_result.block_reason or f"Blocked by hook: {tool_name}"
171
+ logger.info(
172
+ f"[query] Tool {tool_name} blocked by PreToolUse hook",
173
+ extra={"tool_use_id": tool_use_id, "reason": block_reason},
174
+ )
175
+ yield tool_result_message(tool_use_id, f"Hook blocked: {block_reason}", is_error=True)
176
+ return
177
+
178
+ # Handle updated input from hooks
179
+ if pre_result.updated_input:
180
+ logger.debug(
181
+ f"[query] PreToolUse hook modified input for {tool_name}",
182
+ extra={"tool_use_id": tool_use_id},
183
+ )
184
+ # Re-parse the input with the updated values
185
+ try:
186
+ parsed_input = tool.input_schema(**pre_result.updated_input)
187
+ tool_input_dict = pre_result.updated_input
188
+ except (ValueError, TypeError) as exc:
189
+ logger.warning(
190
+ f"[query] Failed to apply updated input from hook: {exc}",
191
+ extra={"tool_use_id": tool_use_id},
192
+ )
193
+
194
+ # Add hook context if provided
195
+ if pre_result.additional_context:
196
+ logger.debug(
197
+ f"[query] PreToolUse hook added context for {tool_name}",
198
+ extra={"context": pre_result.additional_context[:100]},
199
+ )
200
+
201
+ tool_output = None
202
+ tool_error = None
203
+
157
204
  try:
158
205
  async for output in tool.call(parsed_input, tool_context):
159
206
  if isinstance(output, ToolProgress):
@@ -164,6 +211,7 @@ async def _run_tool_use_generator(
164
211
  )
165
212
  logger.debug(f"[query] Progress from tool_use_id={tool_use_id}: {output.content}")
166
213
  elif isinstance(output, ToolResult):
214
+ tool_output = output.data
167
215
  result_content = output.result_for_assistant or str(output.data)
168
216
  result_msg = tool_result_message(
169
217
  tool_use_id, result_content, tool_use_result=output.data
@@ -176,6 +224,7 @@ async def _run_tool_use_generator(
176
224
  except CancelledError:
177
225
  raise # Don't suppress task cancellation
178
226
  except (RuntimeError, ValueError, TypeError, OSError, IOError, AttributeError, KeyError) as exc:
227
+ tool_error = str(exc)
179
228
  logger.warning(
180
229
  "Error executing tool '%s': %s: %s",
181
230
  tool_name, type(exc).__name__, exc,
@@ -183,6 +232,11 @@ async def _run_tool_use_generator(
183
232
  )
184
233
  yield tool_result_message(tool_use_id, f"Error executing tool: {str(exc)}", is_error=True)
185
234
 
235
+ # Run PostToolUse hooks
236
+ await hook_manager.run_post_tool_use_async(
237
+ tool_name, tool_input_dict, tool_response=tool_output, tool_use_id=tool_use_id
238
+ )
239
+
186
240
 
187
241
  def _group_tool_calls_by_concurrency(prepared_calls: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
188
242
  """Group consecutive tool calls by their concurrency safety."""
@@ -624,12 +678,12 @@ async def query_llm(
624
678
  )
625
679
  duration_ms = (time.time() - start_time) * 1000
626
680
  context_error = detect_context_length_error(e)
627
- metadata = None
681
+ error_metadata: Optional[Dict[str, Any]] = None
628
682
  content = f"Error querying AI model: {str(e)}"
629
683
 
630
684
  if context_error:
631
685
  content = f"The request exceeded the model's context window. {context_error.message}"
632
- metadata = {
686
+ error_metadata = {
633
687
  "context_length_exceeded": True,
634
688
  "context_length_provider": context_error.provider,
635
689
  "context_length_error_code": context_error.error_code,
@@ -645,7 +699,7 @@ async def query_llm(
645
699
  )
646
700
 
647
701
  error_msg = create_assistant_message(
648
- content=content, duration_ms=duration_ms, metadata=metadata
702
+ content=content, duration_ms=duration_ms, metadata=error_metadata
649
703
  )
650
704
  error_msg.is_api_error_message = True
651
705
  return error_msg
@@ -1042,7 +1096,10 @@ async def query(
1042
1096
  return
1043
1097
 
1044
1098
  # Update messages for next iteration
1045
- messages = messages + [result.assistant_message] + result.tool_results
1099
+ if result.assistant_message is not None:
1100
+ messages = messages + [result.assistant_message] + result.tool_results # type: ignore[operator]
1101
+ else:
1102
+ messages = messages + result.tool_results # type: ignore[operator]
1046
1103
  logger.debug(
1047
1104
  f"[query] Continuing loop with {len(messages)} messages after tools; "
1048
1105
  f"tool_results_count={len(result.tool_results)}"
@@ -652,7 +652,7 @@ build projects, run tests, and interact with the file system."""
652
652
  # Emit progress updates for newly received output chunks
653
653
  while not queue.empty():
654
654
  label, text = queue.get_nowait()
655
- yield ToolProgress(content=f"{label}: {text}")
655
+ yield ToolProgress(content=f"{label}: {text}") # type: ignore[misc]
656
656
 
657
657
  # Report progress at intervals
658
658
  if now - last_progress_time >= PROGRESS_INTERVAL_SECONDS:
@@ -660,7 +660,7 @@ build projects, run tests, and interact with the file system."""
660
660
  if combined_output:
661
661
  preview = get_last_n_lines(combined_output, 5)
662
662
  elapsed = format_duration((now - start_time) * 1000)
663
- yield ToolProgress(content=f"Running... ({elapsed})\n{preview}")
663
+ yield ToolProgress(content=f"Running... ({elapsed})\n{preview}") # type: ignore[misc]
664
664
  last_progress_time = now
665
665
 
666
666
  # Check timeout
@@ -907,14 +907,14 @@ build projects, run tests, and interact with the file system."""
907
907
 
908
908
  while not queue.empty():
909
909
  label, text = queue.get_nowait()
910
- yield ToolProgress(content=f"{label}: {text}")
910
+ yield ToolProgress(content=f"{label}: {text}") # type: ignore[misc]
911
911
 
912
912
  if now - last_progress_time >= PROGRESS_INTERVAL_SECONDS:
913
913
  combined_output = "".join(stdout_lines + stderr_lines)
914
914
  if combined_output:
915
915
  preview = get_last_n_lines(combined_output, 5)
916
916
  elapsed = format_duration((now - start) * 1000)
917
- yield ToolProgress(content=f"Running... ({elapsed})\n{preview}")
917
+ yield ToolProgress(content=f"Running... ({elapsed})\n{preview}") # type: ignore[misc]
918
918
  last_progress_time = now
919
919
 
920
920
  if deadline is not None and now >= deadline:
@@ -18,7 +18,7 @@ from ripperdoc.core.tool import (
18
18
  )
19
19
  from ripperdoc.utils.log import get_logger
20
20
  from ripperdoc.utils.file_watch import record_snapshot
21
- from ripperdoc.utils.path_ignore import check_path_for_tool, is_path_ignored
21
+ from ripperdoc.utils.path_ignore import check_path_for_tool
22
22
 
23
23
  logger = get_logger()
24
24
 
@@ -325,7 +325,7 @@ async def summarize_conversation(
325
325
  user_content = f"{user_prompt}\n\nHere is the conversation to summarize:\n\n{transcript}"
326
326
 
327
327
  assistant_response = await query_llm(
328
- messages=[{"role": "user", "content": user_content}],
328
+ messages=[create_user_message(user_content)],
329
329
  system_prompt=system_prompt,
330
330
  tools=[],
331
331
  max_thinking_tokens=0,
@@ -346,7 +346,7 @@ async def compact_conversation(
346
346
  protocol: str = "anthropic",
347
347
  tail_count: int = RECENT_MESSAGES_AFTER_COMPACT,
348
348
  attachment_provider: Optional[Callable[[], List[ConversationMessage]]] = None,
349
- ) -> Union[CompactionResult, CompactionError]:
349
+ ) -> Union["CompactionResult", "CompactionError"]:
350
350
  """Compact a conversation by summarizing and rebuilding.
351
351
 
352
352
  This is a pure logic function with no UI dependencies.
@@ -462,7 +462,7 @@ class ConversationCompactor:
462
462
  custom_instructions: str,
463
463
  protocol: str = "anthropic",
464
464
  tail_count: int = RECENT_MESSAGES_AFTER_COMPACT,
465
- ) -> Optional[CompactionResult]:
465
+ ) -> Optional["CompactionResult"]: # type: ignore[valid-type]
466
466
  """Compact the conversation. Returns None on error."""
467
467
  result = await compact_conversation(
468
468
  messages=messages,
@@ -11,8 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import re
13
13
  from pathlib import Path
14
- from typing import Dict, List, Optional, Set, Tuple
15
- from functools import lru_cache
14
+ from typing import Any, Dict, List, Optional, Set, Tuple
16
15
 
17
16
  from ripperdoc.utils.git_utils import (
18
17
  get_git_root,
@@ -361,7 +360,7 @@ class IgnoreFilter:
361
360
 
362
361
  return result
363
362
 
364
- def test(self, path: str) -> Dict[str, any]:
363
+ def test(self, path: str) -> Dict[str, Any]:
365
364
  """Check if a path should be ignored and return details.
366
365
 
367
366
  Returns:
@@ -369,7 +368,7 @@ class IgnoreFilter:
369
368
  """
370
369
  path = path.replace("\\", "/").strip("/")
371
370
 
372
- result = {"ignored": False, "rule": None}
371
+ result: Dict[str, Any] = {"ignored": False, "rule": None}
373
372
 
374
373
  for pattern, is_negation in self._patterns:
375
374
  if pattern.search(path):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripperdoc
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: AI-powered terminal assistant for coding tasks
5
5
  Author: Ripperdoc Team
6
6
  License: Apache-2.0
@@ -35,9 +35,28 @@ Requires-Dist: black>=23.0.0; extra == "dev"
35
35
  Requires-Dist: ruff>=0.1.0; extra == "dev"
36
36
  Dynamic: license-file
37
37
 
38
- # Ripperdoc - AI-Powered Terminal Assistant
38
+ <div align="center">
39
39
 
40
- Ripperdoc is an AI-powered terminal assistant for coding tasks, providing an interactive interface for AI-assisted development, file management, and command execution.
40
+ # Ripperdoc
41
+
42
+ _an open-source, extensible AI coding agent that runs in your terminal_
43
+
44
+ <p align="center">
45
+ <a href="https://opensource.org/licenses/Apache-2.0">
46
+ <img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg">
47
+ </a>
48
+ <a href="https://www.python.org/downloads/">
49
+ <img src="https://img.shields.io/badge/python-3.10+-blue.svg">
50
+ </a>
51
+ <a href="https://github.com/quantmew/ripperdoc/stargazers">
52
+ <img src="https://img.shields.io/github/stars/quantmew/ripperdoc.svg" alt="GitHub stars">
53
+ </a>
54
+ </p>
55
+ </div>
56
+
57
+ Ripperdoc is your on-machine AI coding assistant, similar to [Claude Code](https://claude.com/claude-code), [Codex](https://github.com/openai/codex), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Aider](https://github.com/paul-gauthier/aider), and [Goose](https://github.com/block/goose). It can write code, refactor projects, execute shell commands, and manage files - all through natural language conversations in your terminal.
58
+
59
+ Designed for maximum flexibility, Ripperdoc works with **any LLM** (Anthropic Claude, OpenAI, DeepSeek, local models via OpenAI-compatible APIs), supports **custom hooks** to intercept and control tool execution, and offers both an interactive CLI and a **Python SDK** for headless automation.
41
60
 
42
61
  [中文文档](README_CN.md) | [Contributing](CONTRIBUTING.md) | [Documentation](docs/)
43
62
 
@@ -60,6 +79,8 @@ Ripperdoc is an AI-powered terminal assistant for coding tasks, providing an int
60
79
  - **MCP Server Support** - Integration with Model Context Protocol servers
61
80
  - **Session Management** - Persistent session history and usage tracking
62
81
  - **Jupyter Notebook Support** - Edit .ipynb files directly
82
+ - **Hooks System** - Execute custom scripts at lifecycle events with decision control
83
+ - **Custom Commands** - Define reusable slash commands with parameter substitution
63
84
 
64
85
  ## Installation
65
86
 
@@ -1,8 +1,8 @@
1
- ripperdoc/__init__.py,sha256=2df2ZNxyMS77y0lZ3scL-6v6-11pxxJtRAwIs2hHfG8,66
1
+ ripperdoc/__init__.py,sha256=lrY1tU8qp_EIUO-H5GAvKzGaBf8Z5xcFbY1i2_NBgjE,66
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=ECbi5Rm8prKkNtiv4P191ftSMEMSwYIG2LKC5IJXyIk,14140
5
- ripperdoc/cli/commands/__init__.py,sha256=rSB7hmuMRBfqYCxuUMsdb3-8mloKQyfOTinXU3wm3Uo,2414
4
+ ripperdoc/cli/cli.py,sha256=5H_kIa-kPVyF_KyQA2QROLr6JrHg99RORyHKPrS82qk,14316
5
+ ripperdoc/cli/commands/__init__.py,sha256=Xs69l9O8VglF1z1JkxbmAy_9ylrfXZxFW9G5GiPQBZk,4676
6
6
  ripperdoc/cli/commands/agents_cmd.py,sha256=YDE9oIXPmsPyvkHhq95aXxnneN3PqZ1ZOtcn26cXeO8,10438
7
7
  ripperdoc/cli/commands/base.py,sha256=4KUjxCM04MwbSMUKVNEBph_jeAKPI8b5MHsUFoz7l5g,386
8
8
  ripperdoc/cli/commands/clear_cmd.py,sha256=FDZ0W34VxGyLhLiU4TzukHCyElqsnLwkCmfKJqLfFAQ,366
@@ -12,24 +12,25 @@ ripperdoc/cli/commands/context_cmd.py,sha256=6Yrz3_Oa2NwEsZo4tLK_PFFYP0Vq-amQCMB
12
12
  ripperdoc/cli/commands/cost_cmd.py,sha256=yD9LSqgxVvYNTDPnEHxugjyLWcmbtH5dXim7DIW9zXc,2822
13
13
  ripperdoc/cli/commands/doctor_cmd.py,sha256=q_PO1mnknRysVG7uopiqDWvkIIcvRX2i-JWgfKN-0gQ,7052
14
14
  ripperdoc/cli/commands/exit_cmd.py,sha256=B0CNKQos2eRC4LSjizLdKsFYzFfwRkrUur6Afu3Fh9M,334
15
- ripperdoc/cli/commands/help_cmd.py,sha256=iz1vR-rmWsvvfzdebLiIWEWrcMZo5_Eb55_wLr4Ufno,508
15
+ ripperdoc/cli/commands/help_cmd.py,sha256=jyK6U2bsGEIwFpu08slVHKfxRyS3oblnRXdqSgU_W4w,978
16
+ ripperdoc/cli/commands/hooks_cmd.py,sha256=9kTMXl-7vR9Y63Dm8iieuRK5jYnnlsqWKG5NvDsWUxU,21228
16
17
  ripperdoc/cli/commands/mcp_cmd.py,sha256=ZCnswx0TIiaiUUsIX7NpHaLZLZtvlUhBnN12s_ZtPCA,2424
17
18
  ripperdoc/cli/commands/memory_cmd.py,sha256=gDvRr_-U1gMrOdC3OvujYLL5_CUgyZpwaJdytRP5CBM,6549
18
19
  ripperdoc/cli/commands/models_cmd.py,sha256=p6IeV_K9BjOahmtqmI2Gu7xsqRagVsIPYy7FEeuKQWQ,16135
19
- ripperdoc/cli/commands/permissions_cmd.py,sha256=3ERLLYTLUFW7jV7uo29i6SmLxrizpK4LXayLrPZbB1U,10827
20
- ripperdoc/cli/commands/resume_cmd.py,sha256=Z1jptNhub2WepKL-XPmd8KfDEvU90qjdtpVUajuAZgQ,4122
20
+ ripperdoc/cli/commands/permissions_cmd.py,sha256=k2n82VxlESxM7u5TUrUh85NM-n0JHjqnJxzeAaHpDL0,11325
21
+ ripperdoc/cli/commands/resume_cmd.py,sha256=pFuo3_S_6l3F8usQ9NfyJvc-nBliKW9ct8Rma8YXPlA,4121
21
22
  ripperdoc/cli/commands/status_cmd.py,sha256=yM_c_GgoAL7CMH_ucGSwUhlbHggxYuvCEb4AXtpN-8s,5534
22
23
  ripperdoc/cli/commands/tasks_cmd.py,sha256=QrRF9MKg6LIH9BQz5E39KKdrwMiI3HTvI-c14aM7BU0,8815
23
24
  ripperdoc/cli/commands/todos_cmd.py,sha256=7Q0B1NVqGtB3R29ndbn4m0VQQm-YQ7d4Wlk7vJ7dLQI,1848
24
25
  ripperdoc/cli/commands/tools_cmd.py,sha256=3cMi0vN4mAUhpKqJtRgNvZfcKzRPaMs_pkYYXlyvSSU,384
25
26
  ripperdoc/cli/ui/__init__.py,sha256=TxSzTYdITlrYmYVfins_w_jzPqqWRpqky5u1ikwvmtM,43
26
27
  ripperdoc/cli/ui/context_display.py,sha256=3ezdtHVwltkPQ5etYwfqUh-fjnpPu8B3P81UzrdHxZs,10020
27
- ripperdoc/cli/ui/file_mention_completer.py,sha256=rFVNgTUulZYUZpBq9rnK7bOPp8_NnFokSm2zS3AmCTY,8951
28
+ ripperdoc/cli/ui/file_mention_completer.py,sha256=U6uZbhCamC-cJTVCbblcXvPqmkaevNgKotMC_ftssug,11648
28
29
  ripperdoc/cli/ui/helpers.py,sha256=DmgMMouyQdesjQ5RsErwsRCKVdWiDJnpqJjv90a3neE,2545
29
- ripperdoc/cli/ui/interrupt_handler.py,sha256=bHYBbReu8uzVkHj1kEnL09ILt6b1hdc4iK76ZVT4t84,5922
30
- ripperdoc/cli/ui/message_display.py,sha256=vMOCX8KUPP5P1CSHyZgojj69AyIZAk-Czlcgx-gOSvc,10367
31
- ripperdoc/cli/ui/panels.py,sha256=204DvcXwye75dkSiwpE0lGb7u0SnRLLXd0jY9kT84NU,1753
32
- ripperdoc/cli/ui/rich_ui.py,sha256=yk0ZS7xGzw_NGDOEMvT99XSpHdwVWhprupkaEBxv7Q4,43941
30
+ ripperdoc/cli/ui/interrupt_handler.py,sha256=dg15njl4NsFQvwAtxKPETeubuYl5OwiV2VlPECW2aNI,5930
31
+ ripperdoc/cli/ui/message_display.py,sha256=FHvfBY9hvf-lroB6SDzHDMS6bxuh57BfK5iBIifs2Ks,10361
32
+ ripperdoc/cli/ui/panels.py,sha256=lzgg7kP8nzJKqGFjE-0UVbr9a1YZ0i2XlkUy6-LcLnk,1875
33
+ ripperdoc/cli/ui/rich_ui.py,sha256=_VOx1zdt_QV23cwwiV6P8FiAA_H-JLIbC6WqYBLgUwM,47066
33
34
  ripperdoc/cli/ui/spinner.py,sha256=XsPRwJ-70InLX9Qw50CEgSHn5oKA5PFIue8Un4edhUk,1449
34
35
  ripperdoc/cli/ui/thinking_spinner.py,sha256=9Et5EqPChfkmkiOO8w1OPs8t-sHaisgjn9A__kEYLyg,2824
35
36
  ripperdoc/cli/ui/tool_renderers.py,sha256=gVuZM083Nys9KWYAFTdmr1vpJm7ardqNhyUZx7KkL6s,11170
@@ -37,15 +38,22 @@ ripperdoc/core/__init__.py,sha256=UemJCA-Y8df1466AX-YbRFj071zKajmqO1mi40YVW2g,40
37
38
  ripperdoc/core/agents.py,sha256=3glBiVM8e9NruCQGYl4inQ6Lt7jWb7C4S44YQXRWWl8,19930
38
39
  ripperdoc/core/commands.py,sha256=NXCkljYbAP4dDoRy-_3semFNWxG4YAk9q82u8FTKH60,835
39
40
  ripperdoc/core/config.py,sha256=fuzXTSSpPFIkzgZJW-tOf18cNeemrot64ihO4cdM79g,20979
41
+ ripperdoc/core/custom_commands.py,sha256=2Voc1u6WSZ-nJYRvGKmUcpNvzLCotacgGupvXUv6RT8,14289
40
42
  ripperdoc/core/default_tools.py,sha256=fHmqIlPIE9qGwmgeYYw-QepKRoQLMhclnCv6Qahews0,3090
41
43
  ripperdoc/core/permissions.py,sha256=_WLWE7Kq-Z5j3zEDAPr8JqdT0fz2oFqNs18NL0qoeWQ,9768
42
- ripperdoc/core/query.py,sha256=wpeQEJGTNwqp5Vvu9KoF-y1sZ-TOx_-xGaNwcDJYhfc,38729
44
+ ripperdoc/core/query.py,sha256=We15XbLl_rAdAgJ5NAufYLNes6kNNeQ7CMj8rcGgBsE,40939
43
45
  ripperdoc/core/query_utils.py,sha256=-lBRL5oAV0p6p6LukpFfBZKEcRdztbzCNOt51pEsKBM,24776
44
46
  ripperdoc/core/skills.py,sha256=XkMt3WPT2_0xfx2qQhEnBbwJ0121aRFmuXLckw3MtVU,10251
45
47
  ripperdoc/core/system_prompt.py,sha256=smhuRfzbvhfzNsQ3D89Mucll16u_40VWpOzKS0kJPFQ,26724
46
48
  ripperdoc/core/tool.py,sha256=Hnnt7FYBGlD6lyAr9XhDgp_LfcP7o3yapzd7t6FPlBE,7813
49
+ ripperdoc/core/hooks/__init__.py,sha256=xw7VJQu1ZB0ENHVqL5xtruBnP3d0FNgrBH6NTL2xYgg,2735
50
+ ripperdoc/core/hooks/config.py,sha256=2KZXpkCGWecg3loQkYZbr5Xh68dDZv-XlTvjwQbl29o,10123
51
+ ripperdoc/core/hooks/events.py,sha256=ZDyP_YaRPKeDz23d6wKbfwlb64poRZDpiuPJaz8ZN9w,17957
52
+ ripperdoc/core/hooks/executor.py,sha256=inOAT2-xB_lXiz0io6c3QV4Wnw9ntl0unf-w0nXPMak,16818
53
+ ripperdoc/core/hooks/integration.py,sha256=Gb3ADGoTTYWDrCmkcgjlyLyldu3wRcji77_2VAyuYJw,11213
54
+ ripperdoc/core/hooks/manager.py,sha256=8YqvGPfOL3ianxFz_tnd7c_HbhHeLDHr5cLk1ayKicU,23706
47
55
  ripperdoc/core/providers/__init__.py,sha256=yevsHF0AUI4b6Wiq_401NXewJ3dqe8LUUtQm0TLPPNQ,1911
48
- ripperdoc/core/providers/anthropic.py,sha256=XNv_LhQTBrsFN5dB5rdYrNikBQ6Qq4sEu_PxukJXuks,10257
56
+ ripperdoc/core/providers/anthropic.py,sha256=V5sofBIU1w7bo1FmrrZ_lqVvGRXt9ZkykDa_J0b1xtc,26309
49
57
  ripperdoc/core/providers/base.py,sha256=HNOa3_XWszu6DbI8BYixxV0hnZb9qZ_FU4uinFVRHjU,9271
50
58
  ripperdoc/core/providers/gemini.py,sha256=3U69Gh6hiL8QWsf-nawZJTfh5oeZP5DAmXYDfWtrEQE,24786
51
59
  ripperdoc/core/providers/openai.py,sha256=FiNTCBtFpq0i0K6S0OyC-F-0HssSWJE6jrH3xRsPzD4,20981
@@ -55,12 +63,12 @@ ripperdoc/tools/__init__.py,sha256=RBFz0DDnztDXMqv_zRxFHVY-ez2HYcncx8zh_y-BX6w,4
55
63
  ripperdoc/tools/ask_user_question_tool.py,sha256=QgzmIDVR-wdlLf9fSiVPbRm_8tSaIlGJhuuRYOCGiUU,15446
56
64
  ripperdoc/tools/background_shell.py,sha256=HangGLwN4iy-udo02zUZF3QRPIqOa7sVDesbv2wL9Js,12854
57
65
  ripperdoc/tools/bash_output_tool.py,sha256=ljIOzTOnkbQfe3jExlhpUlMiLT6HpeD-1QI-D1CwHh8,3379
58
- ripperdoc/tools/bash_tool.py,sha256=eiPgyvY7m52xT6JxbFQ_4n3Qk6RZ0yQK5ZMgQMc8QpQ,42669
66
+ ripperdoc/tools/bash_tool.py,sha256=Wua9_R7S0fMfV8SmmI6_m4mpJ7gYdyku34dyTPIRA4o,42757
59
67
  ripperdoc/tools/dynamic_mcp_tool.py,sha256=GERh7qT1mPVivFUIhlFxPNRUwOGNw5CmCnymEwQ-7vk,15662
60
68
  ripperdoc/tools/enter_plan_mode_tool.py,sha256=FYjm_TmBL55pY4GdP7t0ISlqg-Qe3DwpIt-2weL1S4s,7976
61
69
  ripperdoc/tools/exit_plan_mode_tool.py,sha256=3smkwGLTITem5fgA8catSSRay_a1OGQrjs8JF1zDdUQ,5756
62
70
  ripperdoc/tools/file_edit_tool.py,sha256=pF4ZCBFq3vy2DukxGZjBGoyVJIRH-7UY4A-TpFegzdA,13768
63
- ripperdoc/tools/file_read_tool.py,sha256=eGr1C5xPe22TXrJOU7zwXGDjBqAajtYKn6aiIG-KRBs,7413
71
+ ripperdoc/tools/file_read_tool.py,sha256=CvjnYusjolTH-PoQ8CPUVVR37GMLAntT16ffRwqKBto,7396
64
72
  ripperdoc/tools/file_write_tool.py,sha256=SUsFvLVvCwegxEDhL8xpppNRlSl_Hcc1xwNR35FbMqU,7044
65
73
  ripperdoc/tools/glob_tool.py,sha256=oy1S-MrQl57X_wpNXcqXyE4oHI3kmpOQoTYavx3mzEg,5932
66
74
  ripperdoc/tools/grep_tool.py,sha256=n_YNKg8w63JgVJVpg7Qakj75JrymeKByNoapl8IS05U,14125
@@ -78,7 +86,7 @@ ripperdoc/utils/bash_constants.py,sha256=KNn8bzB6nVU5jid9jvjiH4FAu8pP3DZONJ-OknJ
78
86
  ripperdoc/utils/bash_output_utils.py,sha256=3Cf5wKJzRbUesmCNy5HtXIBtv0Z2BxklTfFHJ9q1T3w,1210
79
87
  ripperdoc/utils/coerce.py,sha256=KOPb4KR4p32nwHWG_6GsGHeVZunJyYc2YhC5DLmEZO8,1015
80
88
  ripperdoc/utils/context_length_errors.py,sha256=oyDVr_ME_6j97TLwVZ8bDMb6ISGQx6wEHrY7ckc0GuA,7714
81
- ripperdoc/utils/conversation_compaction.py,sha256=-yRGL2oVweYPNUZljMRH0xMeMeVN_R_vJ4G_wcEcpyg,18338
89
+ ripperdoc/utils/conversation_compaction.py,sha256=m9AHZcRJwbZUHrNYl5Q1esjKQSBKFkARBkO9YNUjm5Q,18364
82
90
  ripperdoc/utils/exit_code_handlers.py,sha256=QtO1iDxVAb8Xp03D6_QixPoJC-RQlcp3ssIo_rm4two,7973
83
91
  ripperdoc/utils/file_watch.py,sha256=CoUIcLuS-VcfxotuxFkel5KpNluMmLGJKDzx26MG3yY,4039
84
92
  ripperdoc/utils/git_utils.py,sha256=Hq-Zx-KPyX4lp_i8ozhic15LyYdX_IfCRm-EyoFu59A,9047
@@ -90,7 +98,7 @@ ripperdoc/utils/message_compaction.py,sha256=ydTtMqo09ds1qTneJrHaFroZ4UtmzzXlXHE
90
98
  ripperdoc/utils/message_formatting.py,sha256=M-2zEkjNEiLyda9x4S0-xyIBSsVOQS3eTzbIE3K7G_Q,7492
91
99
  ripperdoc/utils/messages.py,sha256=OrUK758mmUx2_u_hE5RFLJMp8VeDy-crLGVADwgFO3c,21665
92
100
  ripperdoc/utils/output_utils.py,sha256=R3wqFh9Dko_GK00Exx7XI0DnnldRWMsxZypYX5y6SJo,7448
93
- ripperdoc/utils/path_ignore.py,sha256=2THb4h__cnFz_mZoR6gchDFiIirxSvsbcEKONJhiAQQ,17607
101
+ ripperdoc/utils/path_ignore.py,sha256=m-cbf5I_1HeV7mjwzpb4O9BXGMTFZ4EnFmmguzD7WeY,17596
94
102
  ripperdoc/utils/path_utils.py,sha256=C45Q3OeXnj-0FVEtvf_tdG5922XB6HthUzlUCvfc17Y,1626
95
103
  ripperdoc/utils/prompt.py,sha256=zICNEsA_OtKx8t3zo9tHLXXu6G5K8rPO3jFLKz4j5tg,560
96
104
  ripperdoc/utils/safe_get_cwd.py,sha256=IvG8dIJd2tC5_glUsfeWXkpcF1EHzdkjFtuUGJd669w,815
@@ -105,9 +113,9 @@ ripperdoc/utils/permissions/__init__.py,sha256=33FfOaDLepxJSkp0RLvTdVu7qBXuEcnOo
105
113
  ripperdoc/utils/permissions/path_validation_utils.py,sha256=FWbb21Hwgb9gUPu_WtA14w99UUojVQLVz5HByormoUs,5760
106
114
  ripperdoc/utils/permissions/shell_command_validation.py,sha256=94ylqoDUiTF4v4wEiVS35jFJakyaSxdIFqYKumPTrGk,21205
107
115
  ripperdoc/utils/permissions/tool_permission_utils.py,sha256=6Fdu9-dMKhLsUExjEjoS0EUeRpEVN5UkqyseIC05YmM,9207
108
- ripperdoc-0.2.7.dist-info/licenses/LICENSE,sha256=bRv9UhBor6GhnQDj12RciDcRfu0R7sB-lqCy1sWF75c,9242
109
- ripperdoc-0.2.7.dist-info/METADATA,sha256=WRYXCFZZWO1HLnvzwoqeD1KdlhSZ0gNPE5rnkHDsaNc,6218
110
- ripperdoc-0.2.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
111
- ripperdoc-0.2.7.dist-info/entry_points.txt,sha256=79aohFxFPJmrQ3-Mhain04vb3EWpuc0EyzvDDUnwAu4,81
112
- ripperdoc-0.2.7.dist-info/top_level.txt,sha256=u8LbdTr1a-laHgCO0Utl_R3QGFUhLxWelCDnP2ZgpCU,10
113
- ripperdoc-0.2.7.dist-info/RECORD,,
116
+ ripperdoc-0.2.8.dist-info/licenses/LICENSE,sha256=bRv9UhBor6GhnQDj12RciDcRfu0R7sB-lqCy1sWF75c,9242
117
+ ripperdoc-0.2.8.dist-info/METADATA,sha256=aoAeFiQqkUDQpJEt4buP6G1wKlkCMXI88slI-tGSDGg,7474
118
+ ripperdoc-0.2.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
119
+ ripperdoc-0.2.8.dist-info/entry_points.txt,sha256=79aohFxFPJmrQ3-Mhain04vb3EWpuc0EyzvDDUnwAu4,81
120
+ ripperdoc-0.2.8.dist-info/top_level.txt,sha256=u8LbdTr1a-laHgCO0Utl_R3QGFUhLxWelCDnP2ZgpCU,10
121
+ ripperdoc-0.2.8.dist-info/RECORD,,