ripperdoc 0.2.10__py3-none-any.whl → 0.3.1__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 (73) hide show
  1. ripperdoc/__init__.py +1 -1
  2. ripperdoc/cli/cli.py +164 -57
  3. ripperdoc/cli/commands/__init__.py +4 -0
  4. ripperdoc/cli/commands/agents_cmd.py +3 -7
  5. ripperdoc/cli/commands/doctor_cmd.py +29 -0
  6. ripperdoc/cli/commands/memory_cmd.py +2 -1
  7. ripperdoc/cli/commands/models_cmd.py +61 -5
  8. ripperdoc/cli/commands/resume_cmd.py +1 -0
  9. ripperdoc/cli/commands/skills_cmd.py +103 -0
  10. ripperdoc/cli/commands/stats_cmd.py +4 -4
  11. ripperdoc/cli/commands/status_cmd.py +10 -0
  12. ripperdoc/cli/commands/tasks_cmd.py +6 -3
  13. ripperdoc/cli/commands/themes_cmd.py +139 -0
  14. ripperdoc/cli/ui/file_mention_completer.py +63 -13
  15. ripperdoc/cli/ui/helpers.py +6 -3
  16. ripperdoc/cli/ui/interrupt_listener.py +233 -0
  17. ripperdoc/cli/ui/message_display.py +7 -0
  18. ripperdoc/cli/ui/panels.py +13 -8
  19. ripperdoc/cli/ui/rich_ui.py +513 -84
  20. ripperdoc/cli/ui/spinner.py +68 -5
  21. ripperdoc/cli/ui/tool_renderers.py +10 -9
  22. ripperdoc/cli/ui/wizard.py +18 -11
  23. ripperdoc/core/agents.py +4 -0
  24. ripperdoc/core/config.py +235 -0
  25. ripperdoc/core/default_tools.py +1 -0
  26. ripperdoc/core/hooks/llm_callback.py +0 -1
  27. ripperdoc/core/hooks/manager.py +6 -0
  28. ripperdoc/core/permissions.py +123 -39
  29. ripperdoc/core/providers/openai.py +55 -9
  30. ripperdoc/core/query.py +349 -108
  31. ripperdoc/core/query_utils.py +17 -14
  32. ripperdoc/core/skills.py +1 -0
  33. ripperdoc/core/theme.py +298 -0
  34. ripperdoc/core/tool.py +8 -3
  35. ripperdoc/protocol/__init__.py +14 -0
  36. ripperdoc/protocol/models.py +300 -0
  37. ripperdoc/protocol/stdio.py +1453 -0
  38. ripperdoc/tools/background_shell.py +49 -5
  39. ripperdoc/tools/bash_tool.py +75 -9
  40. ripperdoc/tools/file_edit_tool.py +98 -29
  41. ripperdoc/tools/file_read_tool.py +139 -8
  42. ripperdoc/tools/file_write_tool.py +46 -3
  43. ripperdoc/tools/grep_tool.py +98 -8
  44. ripperdoc/tools/lsp_tool.py +9 -15
  45. ripperdoc/tools/multi_edit_tool.py +26 -3
  46. ripperdoc/tools/skill_tool.py +52 -1
  47. ripperdoc/tools/task_tool.py +33 -8
  48. ripperdoc/utils/file_watch.py +12 -6
  49. ripperdoc/utils/image_utils.py +125 -0
  50. ripperdoc/utils/log.py +30 -3
  51. ripperdoc/utils/lsp.py +9 -3
  52. ripperdoc/utils/mcp.py +80 -18
  53. ripperdoc/utils/message_formatting.py +2 -2
  54. ripperdoc/utils/messages.py +177 -32
  55. ripperdoc/utils/pending_messages.py +50 -0
  56. ripperdoc/utils/permissions/shell_command_validation.py +3 -3
  57. ripperdoc/utils/permissions/tool_permission_utils.py +9 -3
  58. ripperdoc/utils/platform.py +198 -0
  59. ripperdoc/utils/session_heatmap.py +1 -3
  60. ripperdoc/utils/session_history.py +2 -2
  61. ripperdoc/utils/session_stats.py +1 -0
  62. ripperdoc/utils/shell_utils.py +8 -5
  63. ripperdoc/utils/todo.py +0 -6
  64. {ripperdoc-0.2.10.dist-info → ripperdoc-0.3.1.dist-info}/METADATA +49 -17
  65. ripperdoc-0.3.1.dist-info/RECORD +136 -0
  66. {ripperdoc-0.2.10.dist-info → ripperdoc-0.3.1.dist-info}/WHEEL +1 -1
  67. ripperdoc/cli/ui/interrupt_handler.py +0 -174
  68. ripperdoc/sdk/__init__.py +0 -9
  69. ripperdoc/sdk/client.py +0 -408
  70. ripperdoc-0.2.10.dist-info/RECORD +0 -129
  71. {ripperdoc-0.2.10.dist-info → ripperdoc-0.3.1.dist-info}/entry_points.txt +0 -0
  72. {ripperdoc-0.2.10.dist-info → ripperdoc-0.3.1.dist-info}/licenses/LICENSE +0 -0
  73. {ripperdoc-0.2.10.dist-info → ripperdoc-0.3.1.dist-info}/top_level.txt +0 -0
@@ -9,15 +9,16 @@ from __future__ import annotations
9
9
 
10
10
  import os
11
11
  import shutil
12
+ from pathlib import PureWindowsPath
12
13
  from typing import Iterable, List
13
14
 
14
15
  from ripperdoc.utils.log import get_logger
16
+ from ripperdoc.utils.platform import is_windows
15
17
 
16
18
  logger = get_logger()
17
19
 
18
20
  # Common locations to probe if shutil.which misses an otherwise standard path.
19
21
  _COMMON_BIN_DIRS: tuple[str, ...] = ("/bin", "/usr/bin", "/usr/local/bin", "/opt/homebrew/bin")
20
- _IS_WINDOWS = os.name == "nt"
21
22
 
22
23
 
23
24
  def _is_executable(path: str) -> bool:
@@ -93,7 +94,7 @@ def find_suitable_shell() -> str:
93
94
  current_is_bash = "bash" in current_shell
94
95
  current_is_zsh = "zsh" in current_shell
95
96
 
96
- if not _IS_WINDOWS:
97
+ if not is_windows():
97
98
  if (current_is_bash or current_is_zsh) and _is_executable(current_shell):
98
99
  logger.debug("Using SHELL from environment: %s", current_shell)
99
100
  return current_shell
@@ -149,9 +150,11 @@ def build_shell_command(shell_path: str, command: str) -> List[str]:
149
150
  For bash/zsh (including Git Bash), use -lc to run as login shell.
150
151
  For cmd.exe fallback, use /d /s /c.
151
152
  """
152
-
153
- lower = shell_path.lower()
154
- if lower.endswith("cmd.exe") or lower.endswith("\\cmd"):
153
+ # Use PureWindowsPath to correctly extract the shell name from the path.
154
+ # This handles both Windows-style (C:\\Windows\\System32\\cmd.exe) and Unix-style
155
+ # (/usr/bin/bash) paths, as well as simple names (cmd, bash) on any platform.
156
+ shell_name = PureWindowsPath(shell_path).name.lower()
157
+ if shell_name in ("cmd", "cmd.exe"):
155
158
  return [shell_path, "/d", "/s", "/c", command]
156
159
  return [shell_path, "-lc", command]
157
160
 
ripperdoc/utils/todo.py CHANGED
@@ -185,12 +185,6 @@ def format_todo_summary(todos: Sequence[TodoItem]) -> str:
185
185
  f"{stats['by_status']['completed']} completed)."
186
186
  )
187
187
 
188
- next_item = get_next_actionable(todos)
189
- if next_item:
190
- summary += f" Next to tackle: {next_item.content} (id: {next_item.id}, status: {next_item.status})."
191
- elif stats["total"] == 0:
192
- summary += " No todos stored yet."
193
-
194
188
  return summary
195
189
 
196
190
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripperdoc
3
- Version: 0.2.10
3
+ Version: 0.3.1
4
4
  Summary: AI-powered terminal assistant for coding tasks
5
5
  Author: Ripperdoc Team
6
6
  License: Apache-2.0
@@ -23,10 +23,11 @@ Requires-Dist: python-dotenv>=1.0.0
23
23
  Requires-Dist: aiofiles>=23.0.0
24
24
  Requires-Dist: prompt-toolkit>=3.0.0
25
25
  Requires-Dist: PyYAML>=6.0.0
26
- Requires-Dist: mcp[cli]>=1.22.0
26
+ Requires-Dist: mcp[cli]>=1.25.0
27
27
  Requires-Dist: json_repair>=0.54.2
28
28
  Requires-Dist: tiktoken>=0.7.0
29
29
  Requires-Dist: google-genai>=0.3.0
30
+ Requires-Dist: charset-normalizer>=3.0.0
30
31
  Provides-Extra: dev
31
32
  Requires-Dist: pytest>=7.0.0; extra == "dev"
32
33
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -99,27 +100,22 @@ pip install git+https://github.com/quantmew/ripperdoc.git
99
100
  Or install from source:
100
101
  ```bash
101
102
  # Clone the repository
102
- git clone <repository-url>
103
- cd Ripperdoc
103
+ git clone https://github.com/quantmew/ripperdoc.git
104
+ cd ripperdoc
104
105
 
105
106
  # Install from source
106
107
  pip install -e .
107
108
  ```
108
109
 
109
- ### Configuration
110
110
 
111
- Set your API key as an environment variable:
112
- ```bash
113
- export OPENAI_API_KEY="your-api-key-here"
114
- # or for Anthropic Claude
115
- export ANTHROPIC_API_KEY="your-api-key-here"
116
- ```
117
111
 
118
112
  ## Usage
119
113
 
120
114
  ### Interactive Mode (Recommended)
121
115
  ```bash
122
116
  ripperdoc
117
+ # or use the short alias
118
+ rd
123
119
  ```
124
120
 
125
121
  This launches an interactive session where you can:
@@ -128,6 +124,13 @@ This launches an interactive session where you can:
128
124
  - Execute commands
129
125
  - Navigate and explore files
130
126
 
127
+ **Options:**
128
+ - `--yolo` - Skip permission prompts (safe mode is on by default)
129
+ - `--model <model_name>` - Specify a model (e.g., `claude-sonnet-4-20250514`, `gpt-4o`)
130
+ - `--tools <tool_list>` - Filter available tools (comma-separated, or "" for none)
131
+ - `--no-mcp` - Disable MCP server integration
132
+ - `--verbose` - Enable verbose logging
133
+
131
134
  ### Python SDK (headless)
132
135
 
133
136
  Use Ripperdoc without the terminal UI via the new Python SDK. See [docs/SDK_USAGE.md](docs/SDK_USAGE.md) for examples of the one-shot `query` helper and the session-based `RipperdocClient`. 中文指南见 [docs/SDK_USAGE_CN.md](docs/SDK_USAGE_CN.md)。
@@ -143,38 +146,67 @@ See the [examples/](examples/) directory for complete SDK usage examples.
143
146
 
144
147
  ### Safe Mode Permissions
145
148
 
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).
149
+ Safe mode is enabled by default. When prompted:
150
+ - Type `y` or `yes` to allow a single operation
151
+ - Type `a` or `always` to allow all operations of that type for the session
152
+ - Type `n` or `no` to deny the operation
153
+
154
+ Use `--yolo` flag to skip all permission prompts:
155
+ ```bash
156
+ ripperdoc --yolo
157
+ ```
147
158
 
148
159
  ### Agent Skills
149
160
 
150
161
  Extend Ripperdoc with reusable Skill bundles:
151
162
 
152
- - Personal skills live in `~/.ripperdoc/skills/<skill-name>/SKILL.md`
153
- - Project skills live in `.ripperdoc/skills/<skill-name>/SKILL.md` and can be checked into git
154
- - Each `SKILL.md` starts with YAML frontmatter (`name`, `description`, optional `allowed-tools`, `model`, `max-thinking-tokens`, `disable-model-invocation`) followed by the instructions; add supporting files alongside it
155
- - Model and max-thinking-token hints from skills are applied automatically for the rest of the session after you load them with the `Skill` tool
156
- - Ripperdoc exposes skill names/descriptions in the system prompt and loads full content on demand via the `Skill` tool
163
+ - **Personal skills**: `~/.ripperdoc/skills/<skill-name>/SKILL.md`
164
+ - **Project skills**: `.ripperdoc/skills/<skill-name>/SKILL.md` (can be checked into git)
165
+ - Each `SKILL.md` starts with YAML frontmatter:
166
+ - `name` - Skill identifier
167
+ - `description` - What the skill does
168
+ - `allowed-tools` (optional) - Restrict which tools the skill can use
169
+ - `model` (optional) - Suggest a specific model for this skill
170
+ - `max-thinking-tokens` (optional) - Control thinking budget
171
+ - `disable-model-invocation` (optional) - Use skill without calling the model
172
+ - Add supporting files alongside `SKILL.md` as needed
173
+ - Skills are auto-discovered and loaded on demand via the `Skill` tool
174
+
175
+ **Built-in skills:** PDF manipulation (`pdf`), PowerPoint (`pptx`), Excel (`xlsx`)
157
176
 
158
177
  ## Examples
159
178
 
160
179
  ### Code Analysis
161
180
  ```
162
181
  > Can you explain what this function does?
182
+ > Find all references to the `parse_config` function
163
183
  ```
164
184
 
165
185
  ### File Operations
166
186
  ```
167
187
  > Read the main.py file and suggest improvements
188
+ > Create a new component called UserProfile.tsx
189
+ > Update all imports to use the new package structure
168
190
  ```
169
191
 
170
192
  ### Code Generation
171
193
  ```
172
194
  > Create a new Python script that implements a REST API client
195
+ > Generate unit tests for the auth module
196
+ > Add error handling to the database connection code
173
197
  ```
174
198
 
175
199
  ### Project Navigation
176
200
  ```
177
201
  > Show me all the Python files in the project
202
+ > Find where the user authentication logic is implemented
203
+ > List all API endpoints in the project
204
+ ```
205
+
206
+ ### MCP Integration
207
+ ```
208
+ > What MCP servers are available?
209
+ > Query the context7 documentation for React hooks
178
210
  ```
179
211
 
180
212
  ## Development
@@ -0,0 +1,136 @@
1
+ ripperdoc/__init__.py,sha256=WpPS91f5Gza7_d_r4nyqaZ8bJMFXt486w-h3D1Ha9qk,66
2
+ ripperdoc/__main__.py,sha256=1Avq2MceBfwUlNsfasC8n4dqVL_V56Bl3DRsnY4_Nxk,370
3
+ ripperdoc/cli/__init__.py,sha256=03wf6gXBcEgXJrDJS-W_5BEG_DdJ_ep7CxQFPML-73g,35
4
+ ripperdoc/cli/cli.py,sha256=OZgN803jYmzdKxynHWG7VNwd7dQhM1KdYakXDyMlZT8,21826
5
+ ripperdoc/cli/commands/__init__.py,sha256=Up-tTlq3lhxDt-YEdiOoHwfRyIdb8nlL5waECzRg-xY,4858
6
+ ripperdoc/cli/commands/agents_cmd.py,sha256=agDTXwZWSr-fmtp_afuD1kMTA1NalMHtoKQ3eVtJMFs,15699
7
+ ripperdoc/cli/commands/base.py,sha256=4KUjxCM04MwbSMUKVNEBph_jeAKPI8b5MHsUFoz7l5g,386
8
+ ripperdoc/cli/commands/clear_cmd.py,sha256=iyOLWtgYwJKNjG-el2mwFRA3VWGIXrNNLl32Xdwpq9o,584
9
+ ripperdoc/cli/commands/compact_cmd.py,sha256=uR_nB1OX7cUL1TOJoefwdO31Qfyjd0nZSSttErqUxbA,473
10
+ ripperdoc/cli/commands/config_cmd.py,sha256=QcFYOOmNFSHmw6K2vY_wfKrYXi8PSzz6koJFREJoF_c,884
11
+ ripperdoc/cli/commands/context_cmd.py,sha256=tM8o2ZfX-axFYaFLsWOTSER_Yevk3ANr2numHfuh2UE,5232
12
+ ripperdoc/cli/commands/cost_cmd.py,sha256=yD9LSqgxVvYNTDPnEHxugjyLWcmbtH5dXim7DIW9zXc,2822
13
+ ripperdoc/cli/commands/doctor_cmd.py,sha256=w-AVph1fvmlnbuTeUdAL19zikj4MmNRxmXlhcvR2SXQ,8188
14
+ ripperdoc/cli/commands/exit_cmd.py,sha256=lEGLMVozoOM2Ea_Yw-sbaIvAbfb_Nx3UTpC49ga-MZ8,376
15
+ ripperdoc/cli/commands/help_cmd.py,sha256=jyK6U2bsGEIwFpu08slVHKfxRyS3oblnRXdqSgU_W4w,978
16
+ ripperdoc/cli/commands/hooks_cmd.py,sha256=-ixQhKb1CX2c7_2zDdAqXV9ThnMf31UH3a9UBERD9mw,21026
17
+ ripperdoc/cli/commands/mcp_cmd.py,sha256=ZCnswx0TIiaiUUsIX7NpHaLZLZtvlUhBnN12s_ZtPCA,2424
18
+ ripperdoc/cli/commands/memory_cmd.py,sha256=BsYYUosmnIs92mwgHH1e3DdzOj83iJ5_AJ8kBeSkRCw,6594
19
+ ripperdoc/cli/commands/models_cmd.py,sha256=LYebi7s9kv6xxlSqX3Pb1AsWY9S-xp0ikdtPVECinvo,18635
20
+ ripperdoc/cli/commands/permissions_cmd.py,sha256=aIMIvmt78nB2Q-Qa5ojqClUOsPRcoyeDIuhOJX8xg2k,11559
21
+ ripperdoc/cli/commands/resume_cmd.py,sha256=Pil2gJIkjf9fIFrxTyVXTwS7kzsg14CVg1SNQPZYAmw,4295
22
+ ripperdoc/cli/commands/skills_cmd.py,sha256=OFDLy2g_o_GDzdatk18E-Xv4bhVG80MQUr_PQLmTveQ,3496
23
+ ripperdoc/cli/commands/stats_cmd.py,sha256=I6nFZndBy_VRcUAxXAjplSjIzA-5UqRK4HlQJbpHydg,7857
24
+ ripperdoc/cli/commands/status_cmd.py,sha256=9pI_GF4mxR9cQXF-HEURZb0qO6GWp2hcy3rzaVW98II,5962
25
+ ripperdoc/cli/commands/tasks_cmd.py,sha256=tghFKy0ENeJ9Mf0h5Waceq8l9JXP5xTLsJHP4ZkibbQ,9044
26
+ ripperdoc/cli/commands/themes_cmd.py,sha256=b3Ti-KmRF9XVk1jtS7bkz5F8317yu1fyp1I89SSpfqI,4019
27
+ ripperdoc/cli/commands/todos_cmd.py,sha256=7Q0B1NVqGtB3R29ndbn4m0VQQm-YQ7d4Wlk7vJ7dLQI,1848
28
+ ripperdoc/cli/commands/tools_cmd.py,sha256=3cMi0vN4mAUhpKqJtRgNvZfcKzRPaMs_pkYYXlyvSSU,384
29
+ ripperdoc/cli/ui/__init__.py,sha256=TxSzTYdITlrYmYVfins_w_jzPqqWRpqky5u1ikwvmtM,43
30
+ ripperdoc/cli/ui/context_display.py,sha256=3ezdtHVwltkPQ5etYwfqUh-fjnpPu8B3P81UzrdHxZs,10020
31
+ ripperdoc/cli/ui/file_mention_completer.py,sha256=ysNqZieQVlUb7DC4CP_FMKNeTG8AIqKLmJgl6HJBURo,13649
32
+ ripperdoc/cli/ui/helpers.py,sha256=iM7kMb-fMTO6n4_MDVrESE1P-Y7w8PXiIhCCLp5yyA4,2618
33
+ ripperdoc/cli/ui/interrupt_listener.py,sha256=m9NeyOSJ3f-zuLE4YzUUEoKuuxo7oBKuvY5Z1WabjoE,7223
34
+ ripperdoc/cli/ui/message_display.py,sha256=eaS80PBo9QfgWT-MX4jfQOjbt0vwQ4w1MDGKBWPrQOs,10856
35
+ ripperdoc/cli/ui/panels.py,sha256=kaf3sDG2AIcrdrqbASnqGYHySfSeMNozGFhqvZ4bTJs,2207
36
+ ripperdoc/cli/ui/provider_options.py,sha256=Ic30K1ev8w2oEcha4IjDYSoxw1tyCVB4hLoQpS_Y_5E,8369
37
+ ripperdoc/cli/ui/rich_ui.py,sha256=LkE22XdNE4sooX-SyVUec7qX_XDTHjExFTwhqj1MPY4,76870
38
+ ripperdoc/cli/ui/spinner.py,sha256=IlmMgyk-eA6Bk4fXDjbWs3AFL2iFPeWmnS0q28tWOgU,5175
39
+ ripperdoc/cli/ui/thinking_spinner.py,sha256=3zmxj3vd-1njdiHF2Afsm7RGiRl7645AEc7fTLKgAbU,2805
40
+ ripperdoc/cli/ui/tool_renderers.py,sha256=7ACcZoKU91kvFsk3YdP2TzibfI-W_F1dv-Ksr68rrYE,11280
41
+ ripperdoc/cli/ui/wizard.py,sha256=bFKJeQStJ2Opq_ih9tgchkJP14bMvI-PX-zHVwzMiy8,7423
42
+ ripperdoc/core/__init__.py,sha256=UemJCA-Y8df1466AX-YbRFj071zKajmqO1mi40YVW2g,40
43
+ ripperdoc/core/agents.py,sha256=Xl1HEJz8v-xIEhzgObeysgrz-SQBYbxYVRI4CvRl-w8,20539
44
+ ripperdoc/core/commands.py,sha256=NXCkljYbAP4dDoRy-_3semFNWxG4YAk9q82u8FTKH60,835
45
+ ripperdoc/core/config.py,sha256=4KvaGKG4bf9wX6haPvxJllokdpPbJVvfTIMfsC0maag,29936
46
+ ripperdoc/core/custom_commands.py,sha256=2BMYiBq15FDjl3aOa3styN6nARyfU7xFAb4ly2Vsp-w,14254
47
+ ripperdoc/core/default_tools.py,sha256=mpb9mqmrAjMqgcmpRU_uQF1G6vpYbp8nkiwrh71KQyM,5250
48
+ ripperdoc/core/permissions.py,sha256=7Fj44HB4yJj5bm54T_c85sdzjMFWWg_5kXm3CZqklI4,18424
49
+ ripperdoc/core/query.py,sha256=dC_sW2D2bPLI6nPM3VxFpTBA4fYYHHRNmE6zvy93pus,62057
50
+ ripperdoc/core/query_utils.py,sha256=2OQYeobFD-24NG0CZnnm3D5HCJ7r8AOB08I-wpQhH1A,24888
51
+ ripperdoc/core/skills.py,sha256=NGP_QFzL2SBkz8wZBGxNA_6Gt8qkRIZtJDnPs_DayxE,10511
52
+ ripperdoc/core/system_prompt.py,sha256=d3GNCsJ_mdIojpWg1Wc0gRDC_gzRDI5G4tQN_gxhRdo,26752
53
+ ripperdoc/core/theme.py,sha256=cQ0LTDxP2whFEsTRi421p1Rs7Epe9rlv-HgFirvIxzM,8427
54
+ ripperdoc/core/tool.py,sha256=0ihPcsgr0Dz5kl9-8hCU-xaRui5_WCjIOnVTpshc8k0,8529
55
+ ripperdoc/core/hooks/__init__.py,sha256=xw7VJQu1ZB0ENHVqL5xtruBnP3d0FNgrBH6NTL2xYgg,2735
56
+ ripperdoc/core/hooks/config.py,sha256=wE_eMHDZu9-yHGyJ45ySL1_l2yx7B8i4jiThs78W6Zc,10085
57
+ ripperdoc/core/hooks/events.py,sha256=UGBDdec52abi2e6ox8ncsLeDo3t9ZJesymKaPwxNDAE,18403
58
+ ripperdoc/core/hooks/executor.py,sha256=3MdiCdc4Bn2xHcTlRT72BajrgAZ5roKck0NBnO4Re0c,16781
59
+ ripperdoc/core/hooks/integration.py,sha256=IzkOSpaMjC397zKdKO1jTR0uyzOet-eCwPLuXwTYOts,11082
60
+ ripperdoc/core/hooks/llm_callback.py,sha256=TsttZ8Q7c8-36ebbI8nTyYcKbA7Kr9rmY5UO2gzuL-c,1733
61
+ ripperdoc/core/hooks/manager.py,sha256=dfHhB8f5US8ZN2a0JbzD5bQZHsnBS6Zs8xVZ2IcSsrg,25092
62
+ ripperdoc/core/providers/__init__.py,sha256=yevsHF0AUI4b6Wiq_401NXewJ3dqe8LUUtQm0TLPPNQ,1911
63
+ ripperdoc/core/providers/anthropic.py,sha256=B967szN1Thc0K1Iv8TvKWcVKktCevBmShftupVag8pk,28551
64
+ ripperdoc/core/providers/base.py,sha256=HNOa3_XWszu6DbI8BYixxV0hnZb9qZ_FU4uinFVRHjU,9271
65
+ ripperdoc/core/providers/gemini.py,sha256=Fs-dShsmIVBFfz-jg4fBjvQyrxVnZ5yx4ALcES-l5Sg,27089
66
+ ripperdoc/core/providers/openai.py,sha256=BhvDvkDjeYJMbvI4mbswQXbjdab53oJduku-Yja1zmQ,25561
67
+ ripperdoc/protocol/__init__.py,sha256=rSYu_Y2acRfsAE8gUTG82VnyrK3AFrIfwuJBf2cjHjs,325
68
+ ripperdoc/protocol/models.py,sha256=goNurNKI0eGZeCGmr7KqMPkZMvMOABXWsb-8un0LDlc,7468
69
+ ripperdoc/protocol/stdio.py,sha256=jia8cca3offyOc8kego-MnJfXsZkFwQw0cZZzfeFQqs,58933
70
+ ripperdoc/tools/__init__.py,sha256=RBFz0DDnztDXMqv_zRxFHVY-ez2HYcncx8zh_y-BX6w,42
71
+ ripperdoc/tools/ask_user_question_tool.py,sha256=ZWg5xAdeaRoR98KvvPuKmJH4L2dgzH87VXM4dxKcqBE,15478
72
+ ripperdoc/tools/background_shell.py,sha256=VMJd0G7s1l7qSRvlkRaIbgWmj4N0U_sr0kLzBzVLO2A,21263
73
+ ripperdoc/tools/bash_output_tool.py,sha256=cC5dDmKYmkOTsLCXCcTYgc0loVWtmRobPn0C-I6qO-o,3379
74
+ ripperdoc/tools/bash_tool.py,sha256=m3jfukBBOSQUYJbntekTGGk3FIn1GKaZVvcqMWwLBYE,47598
75
+ ripperdoc/tools/dynamic_mcp_tool.py,sha256=kxxWhp6pP9N8fK3ubu5fHQYQv7aSwxcnaa3C9ZsBbOU,15879
76
+ ripperdoc/tools/enter_plan_mode_tool.py,sha256=LNQv43uWkohiQTYQdsrKbpAfQSJNE_FJ9Y6AM_ckjng,7976
77
+ ripperdoc/tools/exit_plan_mode_tool.py,sha256=XxhEih5EUrcscvkRA9lel54XoVIhl_cCL2xcvMJjtVA,5756
78
+ ripperdoc/tools/file_edit_tool.py,sha256=IHgaWbzR7NKpLJ5XOjxU24c5-RyseVJw-p3QFGtcKJQ,22190
79
+ ripperdoc/tools/file_read_tool.py,sha256=TXEt_Q7RSaTx4qdx3_mOYP704UU6qRccL5_AqPlNFrY,13735
80
+ ripperdoc/tools/file_write_tool.py,sha256=HqOkFYedacNsPatKxVhth0kI5fGh83tm2VxdD_pO-V0,8570
81
+ ripperdoc/tools/glob_tool.py,sha256=eZG4fzahjJsSM8NdmTiVl5nBfDQK7egPg6P7cqOM_1Y,5948
82
+ ripperdoc/tools/grep_tool.py,sha256=bqrliI6e-9cNKIOfxdCv6mBwHr_1BSLwAPSTbOTc4B4,18239
83
+ ripperdoc/tools/kill_bash_tool.py,sha256=_jwnJVCPe8uXZTJd7myh4hWCD-eBuB2XDaYwRCmNSsI,4625
84
+ ripperdoc/tools/ls_tool.py,sha256=JWQucgNOLjrGZwM7imu3GWe5YFwXxdXGoaBr50wDZCQ,15367
85
+ ripperdoc/tools/lsp_tool.py,sha256=atpaaMqf3jld7SyNXtfdM6NkxYrp3KbYZ91oC4EqQlU,21315
86
+ ripperdoc/tools/mcp_tools.py,sha256=BVz8MJhijNnHq1J2LLkZS89wp_pwwnOytH1HYcfkFqQ,23085
87
+ ripperdoc/tools/multi_edit_tool.py,sha256=s9gN3oRPqeYjQxdsfnkfHSIMTrBs09ihV_TYuVqaey8,18446
88
+ ripperdoc/tools/notebook_edit_tool.py,sha256=Y2XkQB4WDbSWeCsO3Ybnsrbcxki99aOv3o23jZ5D1Hw,14445
89
+ ripperdoc/tools/skill_tool.py,sha256=8TcmNxpSGiBXd-ipgoD9BIopKkR13-3U56rkzozazhY,9763
90
+ ripperdoc/tools/task_tool.py,sha256=SQW2yLM0yectI0dVBoaDF0Tp5T4yOYqvHpsFWapOE44,33979
91
+ ripperdoc/tools/todo_tool.py,sha256=eIwF-s1117DrXJ4yXUMwkNs1gfKYNF2OlWzkJAXDzmk,20001
92
+ ripperdoc/tools/tool_search_tool.py,sha256=AeY-tFtWr2IAHTCEnG9kvsRRBqrBd-PJ96oUnFKa3Vs,13890
93
+ ripperdoc/utils/__init__.py,sha256=gdso60znB2hsYZ_YZBKVcuOY3QVfoqD2wHQ4pvr5lSw,37
94
+ ripperdoc/utils/bash_constants.py,sha256=KNn8bzB6nVU5jid9jvjiH4FAu8pP3DZONJ-OknJypAQ,1641
95
+ ripperdoc/utils/bash_output_utils.py,sha256=3Cf5wKJzRbUesmCNy5HtXIBtv0Z2BxklTfFHJ9q1T3w,1210
96
+ ripperdoc/utils/coerce.py,sha256=KOPb4KR4p32nwHWG_6GsGHeVZunJyYc2YhC5DLmEZO8,1015
97
+ ripperdoc/utils/context_length_errors.py,sha256=oyDVr_ME_6j97TLwVZ8bDMb6ISGQx6wEHrY7ckc0GuA,7714
98
+ ripperdoc/utils/conversation_compaction.py,sha256=mbOYwQXXsAqiJ7mBQ25195bXtH3tb5GSU0HUNsvnl1g,18345
99
+ ripperdoc/utils/exit_code_handlers.py,sha256=QtO1iDxVAb8Xp03D6_QixPoJC-RQlcp3ssIo_rm4two,7973
100
+ ripperdoc/utils/file_watch.py,sha256=Oqx5elgE3_z6CSAKdTbIPna_E4SePBqDujpqqANcw9U,11823
101
+ ripperdoc/utils/git_utils.py,sha256=Hq-Zx-KPyX4lp_i8ozhic15LyYdX_IfCRm-EyoFu59A,9047
102
+ ripperdoc/utils/image_utils.py,sha256=52YRLN0NwPEHnL3-smYktx2T8Oh002W0nvZsxI-WFdo,3380
103
+ ripperdoc/utils/json_utils.py,sha256=Bx1pHHu5r7GtvCqFHM3K9EoknFAtYOaCqTn9RN-5qBA,757
104
+ ripperdoc/utils/log.py,sha256=niCsP__DSbgSAJfitmrsSkh3UjPw7NdYHURrx7DLRuU,7197
105
+ ripperdoc/utils/lsp.py,sha256=4_zw1ZzbvMe16kn6U5EHyMyACX-m0Hr0AjoFXi1xsEQ,26844
106
+ ripperdoc/utils/mcp.py,sha256=-8yZD6gc43BqiFmG3intZcyfVhAYusaXgoI8rJUSdGA,22621
107
+ ripperdoc/utils/memory.py,sha256=J_kucw1BBnHQ1qG2_ZzdNysKvS1lrpuMtB5wxJDmXZU,8033
108
+ ripperdoc/utils/message_compaction.py,sha256=FwYxjWc0B7IlzT1VPEyfhwEl8rXbDi37utbEbS5qGWw,22577
109
+ ripperdoc/utils/message_formatting.py,sha256=9uA7c3MMuWTIp0SQAXkvuRsofKlQ_r0xRHD7ln5lR8s,7820
110
+ ripperdoc/utils/messages.py,sha256=qX9vh81hZXQc3vHLTLfuiLrLwMkLc4XKpX7ZnTRzI7I,30987
111
+ ripperdoc/utils/output_utils.py,sha256=R3wqFh9Dko_GK00Exx7XI0DnnldRWMsxZypYX5y6SJo,7448
112
+ ripperdoc/utils/path_ignore.py,sha256=5VOk075Ef9Wz9LhAWjFQuXJfnypxTD0w9cZVoTGko2M,17882
113
+ ripperdoc/utils/path_utils.py,sha256=C45Q3OeXnj-0FVEtvf_tdG5922XB6HthUzlUCvfc17Y,1626
114
+ ripperdoc/utils/pending_messages.py,sha256=75DKO1SgVGMlzC_ycfKNJ_cktj_MFVFy60j4EXeRsM0,1683
115
+ ripperdoc/utils/platform.py,sha256=Q-2hp0ywegEiuxK3TzE5Pg9wuHJiY8tPp9eoEMCtx4Y,5024
116
+ ripperdoc/utils/prompt.py,sha256=zICNEsA_OtKx8t3zo9tHLXXu6G5K8rPO3jFLKz4j5tg,560
117
+ ripperdoc/utils/safe_get_cwd.py,sha256=lYxFJAN7lomoLwTAfMZtyOueotuvhC8TN84NtrPKj1E,827
118
+ ripperdoc/utils/sandbox_utils.py,sha256=G91P8dw2VFcCiCpjXZ4LvzbAPiO8REqMhw39eI5Z4dU,1123
119
+ ripperdoc/utils/session_heatmap.py,sha256=o88ZlL9tFizOEbo92r6RKcfe2hscghVv7wS_REltZIw,8436
120
+ ripperdoc/utils/session_history.py,sha256=S-z5TneMvyLvoITJG4bRKvr4bvh5LWp8rKIuekLaFA4,9716
121
+ ripperdoc/utils/session_stats.py,sha256=xf5KKhNlzocPxMAS5fRUF6YVDH9fpqObfx_ah_iAMqA,9101
122
+ ripperdoc/utils/session_usage.py,sha256=p8_s46zDTzV1qzP4HR4PuZmLeJvSfq9mG_Y5rCnRYyA,3213
123
+ ripperdoc/utils/shell_token_utils.py,sha256=SduoSU-RERJdM_7gBn0urr5UXtl4XOpPgydBd2fwzWg,2500
124
+ ripperdoc/utils/shell_utils.py,sha256=RQ0iMdvv0SQOs9vqDAaklLLOo0inC935DOjmy0iZKYc,5541
125
+ ripperdoc/utils/todo.py,sha256=t0DJMHtwRBGf_uEx9zzDwp2K0TOoOT0NEhFXWm_YoR0,6807
126
+ ripperdoc/utils/token_estimation.py,sha256=qPQbeUwVlafEjzsXw6qMo0hd4Vjb1gCUMAPBouYUASI,1066
127
+ ripperdoc/utils/permissions/__init__.py,sha256=33FfOaDLepxJSkp0RLvTdVu7qBXuEcnOoTHFbEtFOt0,653
128
+ ripperdoc/utils/permissions/path_validation_utils.py,sha256=KOegjWaph8tXU7aqwQXRAxFEzrmRuPvdLb36J1QIPDQ,5772
129
+ ripperdoc/utils/permissions/shell_command_validation.py,sha256=BRi-1OGzr-Wiuxa00Ye6oqPctoJUZBOoRoi6dUiAsfM,33256
130
+ ripperdoc/utils/permissions/tool_permission_utils.py,sha256=YqbYSXyR0RBbLibBtigYb9O9143mDRulsQCh_WwArvY,14653
131
+ ripperdoc-0.3.1.dist-info/licenses/LICENSE,sha256=bRv9UhBor6GhnQDj12RciDcRfu0R7sB-lqCy1sWF75c,9242
132
+ ripperdoc-0.3.1.dist-info/METADATA,sha256=QawsGroIo9Rs4CodXPVMBNbiFmPZrnJE8nMgqNNDEyQ,8651
133
+ ripperdoc-0.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
134
+ ripperdoc-0.3.1.dist-info/entry_points.txt,sha256=79aohFxFPJmrQ3-Mhain04vb3EWpuc0EyzvDDUnwAu4,81
135
+ ripperdoc-0.3.1.dist-info/top_level.txt,sha256=u8LbdTr1a-laHgCO0Utl_R3QGFUhLxWelCDnP2ZgpCU,10
136
+ ripperdoc-0.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,174 +0,0 @@
1
- """Interrupt handling for RichUI.
2
-
3
- This module handles ESC/Ctrl+C key detection during query execution,
4
- including terminal raw mode management.
5
- """
6
-
7
- import asyncio
8
- import contextlib
9
- import sys
10
- from typing import Any, Optional, Set
11
-
12
- from ripperdoc.utils.log import get_logger
13
-
14
- logger = get_logger()
15
-
16
- # Keys that trigger interrupt
17
- INTERRUPT_KEYS: Set[str] = {"\x1b", "\x03"} # ESC, Ctrl+C
18
-
19
-
20
- class InterruptHandler:
21
- """Handles keyboard interrupt detection during async operations."""
22
-
23
- def __init__(self) -> None:
24
- """Initialize the interrupt handler."""
25
- self._query_interrupted: bool = False
26
- self._esc_listener_active: bool = False
27
- self._esc_listener_paused: bool = False
28
- self._stdin_fd: Optional[int] = None
29
- self._stdin_old_settings: Optional[list] = None
30
- self._stdin_in_raw_mode: bool = False
31
- self._abort_callback: Optional[Any] = None
32
-
33
- def set_abort_callback(self, callback: Any) -> None:
34
- """Set the callback to trigger when interrupt is detected."""
35
- self._abort_callback = callback
36
-
37
- @property
38
- def was_interrupted(self) -> bool:
39
- """Check if the last query was interrupted."""
40
- return self._query_interrupted
41
-
42
- def pause_listener(self) -> bool:
43
- """Pause ESC listener and restore cooked terminal mode if we own raw mode.
44
-
45
- Returns:
46
- Previous paused state for later restoration.
47
- """
48
- prev = self._esc_listener_paused
49
- self._esc_listener_paused = True
50
- try:
51
- import termios
52
- except ImportError:
53
- return prev
54
-
55
- if (
56
- self._stdin_fd is not None
57
- and self._stdin_old_settings is not None
58
- and self._stdin_in_raw_mode
59
- ):
60
- with contextlib.suppress(OSError, termios.error, ValueError):
61
- termios.tcsetattr(self._stdin_fd, termios.TCSADRAIN, self._stdin_old_settings)
62
- self._stdin_in_raw_mode = False
63
- return prev
64
-
65
- def resume_listener(self, previous_state: bool) -> None:
66
- """Restore paused state to what it was before a blocking prompt."""
67
- self._esc_listener_paused = previous_state
68
-
69
- async def _listen_for_interrupt_key(self) -> bool:
70
- """Listen for interrupt keys (ESC/Ctrl+C) during query execution.
71
-
72
- Uses raw terminal mode for immediate key detection without waiting
73
- for escape sequences to complete.
74
-
75
- Returns:
76
- True if an interrupt key was pressed.
77
- """
78
- import select
79
- import termios
80
- import tty
81
-
82
- try:
83
- fd = sys.stdin.fileno()
84
- old_settings = termios.tcgetattr(fd)
85
- except (OSError, termios.error, ValueError):
86
- return False
87
-
88
- self._stdin_fd = fd
89
- self._stdin_old_settings = old_settings
90
- raw_enabled = False
91
- try:
92
- while self._esc_listener_active:
93
- if self._esc_listener_paused:
94
- if raw_enabled:
95
- with contextlib.suppress(OSError, termios.error, ValueError):
96
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
97
- raw_enabled = False
98
- self._stdin_in_raw_mode = False
99
- await asyncio.sleep(0.05)
100
- continue
101
-
102
- if not raw_enabled:
103
- tty.setraw(fd)
104
- raw_enabled = True
105
- self._stdin_in_raw_mode = True
106
-
107
- await asyncio.sleep(0.02)
108
- if select.select([sys.stdin], [], [], 0)[0]:
109
- if sys.stdin.read(1) in INTERRUPT_KEYS:
110
- return True
111
- except (OSError, ValueError):
112
- pass
113
- finally:
114
- self._stdin_in_raw_mode = False
115
- with contextlib.suppress(OSError, termios.error, ValueError):
116
- if raw_enabled:
117
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
118
- self._stdin_fd = None
119
- self._stdin_old_settings = None
120
-
121
- return False
122
-
123
- async def _cancel_task(self, task: asyncio.Task) -> None:
124
- """Cancel a task and wait for it to finish."""
125
- if not task.done():
126
- task.cancel()
127
- with contextlib.suppress(asyncio.CancelledError):
128
- await task
129
-
130
- def _trigger_abort(self) -> None:
131
- """Signal the query to abort via callback."""
132
- if self._abort_callback is not None:
133
- self._abort_callback()
134
-
135
- async def run_with_interrupt(self, query_coro: Any) -> bool:
136
- """Run a coroutine with ESC key interrupt support.
137
-
138
- Args:
139
- query_coro: The coroutine to run with interrupt support.
140
-
141
- Returns:
142
- True if interrupted, False if completed normally.
143
- """
144
- self._query_interrupted = False
145
- self._esc_listener_active = True
146
-
147
- query_task = asyncio.create_task(query_coro)
148
- interrupt_task = asyncio.create_task(self._listen_for_interrupt_key())
149
-
150
- try:
151
- done, _ = await asyncio.wait(
152
- {query_task, interrupt_task}, return_when=asyncio.FIRST_COMPLETED
153
- )
154
-
155
- # Check if interrupted
156
- if interrupt_task in done and interrupt_task.result():
157
- self._query_interrupted = True
158
- self._trigger_abort()
159
- await self._cancel_task(query_task)
160
- return True
161
-
162
- # Query completed normally
163
- if query_task in done:
164
- await self._cancel_task(interrupt_task)
165
- with contextlib.suppress(Exception):
166
- query_task.result()
167
- return False
168
-
169
- return False
170
-
171
- finally:
172
- self._esc_listener_active = False
173
- await self._cancel_task(query_task)
174
- await self._cancel_task(interrupt_task)
ripperdoc/sdk/__init__.py DELETED
@@ -1,9 +0,0 @@
1
- """Lightweight Python SDK for using Ripperdoc headlessly."""
2
-
3
- from ripperdoc.sdk.client import (
4
- RipperdocClient,
5
- RipperdocOptions,
6
- query,
7
- )
8
-
9
- __all__ = ["RipperdocClient", "RipperdocOptions", "query"]