ripperdoc 0.2.3__py3-none-any.whl → 0.2.4__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/utils/mcp.py CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import contextvars
7
7
  import json
8
+ import shlex
8
9
  from contextlib import AsyncExitStack
9
10
  from dataclasses import dataclass, field, replace
10
11
  from pathlib import Path
@@ -12,16 +13,16 @@ from typing import Any, Dict, List, Optional
12
13
 
13
14
  from ripperdoc import __version__
14
15
  from ripperdoc.utils.log import get_logger
15
- from ripperdoc.utils.message_compaction import estimate_tokens_from_text
16
+ from ripperdoc.utils.token_estimation import estimate_tokens
16
17
 
17
18
  logger = get_logger()
18
19
 
19
20
  try:
20
- import mcp.types as mcp_types
21
- from mcp.client.session import ClientSession
22
- from mcp.client.sse import sse_client
23
- from mcp.client.stdio import StdioServerParameters, stdio_client
24
- from mcp.client.streamable_http import streamablehttp_client
21
+ import mcp.types as mcp_types # type: ignore[import-not-found]
22
+ from mcp.client.session import ClientSession # type: ignore[import-not-found]
23
+ from mcp.client.sse import sse_client # type: ignore[import-not-found]
24
+ from mcp.client.stdio import StdioServerParameters, stdio_client # type: ignore[import-not-found]
25
+ from mcp.client.streamable_http import streamablehttp_client # type: ignore[import-not-found]
25
26
 
26
27
  MCP_AVAILABLE = True
27
28
  except Exception: # pragma: no cover - handled gracefully at runtime
@@ -97,10 +98,48 @@ def _ensure_str_dict(raw: object) -> Dict[str, str]:
97
98
  return result
98
99
 
99
100
 
101
+ def _normalize_command(
102
+ raw_command: Any, raw_args: Any
103
+ ) -> tuple[Optional[str], List[str]]:
104
+ """Normalize MCP server command/args.
105
+
106
+ Supports:
107
+ - command as list -> first element is executable, rest are args
108
+ - command as string with spaces -> shlex.split into executable/args (when args empty)
109
+ - command as plain string -> used as-is
110
+ """
111
+ args: List[str] = []
112
+ if isinstance(raw_args, list):
113
+ args = [str(a) for a in raw_args]
114
+
115
+ # Command provided as list: treat first token as command.
116
+ if isinstance(raw_command, list):
117
+ tokens = [str(t) for t in raw_command if str(t)]
118
+ if not tokens:
119
+ return None, args
120
+ return tokens[0], tokens[1:] + args
121
+
122
+ if not isinstance(raw_command, str):
123
+ return None, args
124
+
125
+ command_str = raw_command.strip()
126
+ if not command_str:
127
+ return None, args
128
+
129
+ if not args and (" " in command_str or "\t" in command_str):
130
+ try:
131
+ tokens = shlex.split(command_str)
132
+ except ValueError:
133
+ tokens = [command_str]
134
+ if tokens:
135
+ return tokens[0], tokens[1:]
136
+
137
+ return command_str, args
138
+
139
+
100
140
  def _parse_server(name: str, raw: Dict[str, Any]) -> McpServerInfo:
101
141
  server_type = str(raw.get("type") or raw.get("transport") or "").strip().lower()
102
- command = raw.get("command")
103
- args = raw.get("args") if isinstance(raw.get("args"), list) else []
142
+ command, args = _normalize_command(raw.get("command"), raw.get("args"))
104
143
  url = str(raw.get("url") or raw.get("uri") or "").strip() or None
105
144
 
106
145
  if not server_type:
@@ -121,7 +160,7 @@ def _parse_server(name: str, raw: Dict[str, Any]) -> McpServerInfo:
121
160
  type=server_type,
122
161
  url=url,
123
162
  description=description,
124
- command=str(command) if isinstance(command, str) else None,
163
+ command=command,
125
164
  args=[str(a) for a in args] if args else [],
126
165
  env=env,
127
166
  headers=headers,
@@ -482,7 +521,7 @@ def format_mcp_instructions(servers: List[McpServerInfo]) -> str:
482
521
  def estimate_mcp_tokens(servers: List[McpServerInfo]) -> int:
483
522
  """Estimate token usage for MCP instructions."""
484
523
  mcp_text = format_mcp_instructions(servers)
485
- return estimate_tokens_from_text(mcp_text)
524
+ return estimate_tokens(mcp_text)
486
525
 
487
526
 
488
527
  __all__ = [
@@ -3,13 +3,13 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
- import math
7
6
  import os
8
7
  from dataclasses import dataclass
9
8
  from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Union
10
9
 
11
10
  from ripperdoc.core.config import GlobalConfig, ModelProfile, get_global_config
12
11
  from ripperdoc.utils.log import get_logger
12
+ from ripperdoc.utils.token_estimation import estimate_tokens
13
13
  from ripperdoc.utils.messages import (
14
14
  AssistantMessage,
15
15
  MessageContent,
@@ -140,10 +140,8 @@ def _parse_truthy_env_value(value: Optional[str]) -> bool:
140
140
 
141
141
 
142
142
  def estimate_tokens_from_text(text: str) -> int:
143
- """Rough token estimate using a 4-characters-per-token rule."""
144
- if not text:
145
- return 0
146
- return max(1, math.ceil(len(text) / 4))
143
+ """Estimate token count using shared token estimation helper."""
144
+ return estimate_tokens(text)
147
145
 
148
146
 
149
147
  def _stringify_content(content: Union[str, List[MessageContent], None]) -> str:
@@ -0,0 +1,33 @@
1
+ """Shared token estimation utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+
7
+ from ripperdoc.utils.log import get_logger
8
+
9
+ logger = get_logger()
10
+
11
+ # Optional: use tiktoken for accurate counts when available.
12
+ try: # pragma: no cover - optional dependency
13
+ import tiktoken # type: ignore
14
+
15
+ _TIKTOKEN_ENCODING = tiktoken.get_encoding("cl100k_base")
16
+ except Exception: # pragma: no cover - runtime fallback
17
+ _TIKTOKEN_ENCODING = None
18
+
19
+
20
+ def estimate_tokens(text: str) -> int:
21
+ """Estimate token count, preferring tiktoken when available."""
22
+ if not text:
23
+ return 0
24
+ if _TIKTOKEN_ENCODING:
25
+ try:
26
+ return len(_TIKTOKEN_ENCODING.encode(text))
27
+ except Exception:
28
+ logger.debug("[token_estimation] tiktoken encode failed; falling back to heuristic")
29
+ # Heuristic: ~4 characters per token
30
+ return max(1, math.ceil(len(text) / 4))
31
+
32
+
33
+ __all__ = ["estimate_tokens"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripperdoc
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: AI-powered terminal assistant for coding tasks
5
5
  Author: Ripperdoc Team
6
6
  License: Apache-2.0
@@ -25,6 +25,8 @@ Requires-Dist: prompt-toolkit>=3.0.0
25
25
  Requires-Dist: PyYAML>=6.0.0
26
26
  Requires-Dist: mcp[cli]>=1.22.0
27
27
  Requires-Dist: json_repair>=0.54.2
28
+ Requires-Dist: tiktoken>=0.7.0
29
+ Requires-Dist: google-genai>=0.3.0
28
30
  Provides-Extra: dev
29
31
  Requires-Dist: pytest>=7.0.0; extra == "dev"
30
32
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -1,4 +1,4 @@
1
- ripperdoc/__init__.py,sha256=UNSvPbYxqTbZJseto7Btn4nFqfZkKezUBuwPQuI2yOk,66
1
+ ripperdoc/__init__.py,sha256=cIeXlodbjB7z1VygS4LMKBWB7lVrewhglDA7hgBcEGY,66
2
2
  ripperdoc/__main__.py,sha256=7oIFEXI2irIoZ_dhcMd3hCs4Dj8tmMBbwiVACAoeE-k,506
3
3
  ripperdoc/cli/__init__.py,sha256=03wf6gXBcEgXJrDJS-W_5BEG_DdJ_ep7CxQFPML-73g,35
4
4
  ripperdoc/cli/cli.py,sha256=fszg31t6QyWeBEM6PkoidpwamYXvYQScKCSSq8F8uHM,13043
@@ -8,7 +8,7 @@ ripperdoc/cli/commands/base.py,sha256=4KUjxCM04MwbSMUKVNEBph_jeAKPI8b5MHsUFoz7l5
8
8
  ripperdoc/cli/commands/clear_cmd.py,sha256=zSYT0Nn_htZzLWTTQ4E5KWHfRg0Q5CYvRO4e--7thBY,345
9
9
  ripperdoc/cli/commands/compact_cmd.py,sha256=C_qdPTPdg1cOHdmODkaYoRusosgiqRK6c6KID_Gwq0k,330
10
10
  ripperdoc/cli/commands/config_cmd.py,sha256=ebIQk7zUFv353liWfbBSSfPiOaaCR7rQsd_eTw7nsvY,884
11
- ripperdoc/cli/commands/context_cmd.py,sha256=d0KiJyjbuDNXYlfSzVTFmxmkLXJZe3pUDg_Tt67OWqs,4359
11
+ ripperdoc/cli/commands/context_cmd.py,sha256=ri3DbieAG-SqzvgLg8eOKg0t6_IQS4Itp1fOLn4pNAs,4369
12
12
  ripperdoc/cli/commands/cost_cmd.py,sha256=yD9LSqgxVvYNTDPnEHxugjyLWcmbtH5dXim7DIW9zXc,2822
13
13
  ripperdoc/cli/commands/doctor_cmd.py,sha256=JQVexktceKTBMml5MO-V86IsDsSqXcSA8igw2zf3Qpo,6655
14
14
  ripperdoc/cli/commands/exit_cmd.py,sha256=B0CNKQos2eRC4LSjizLdKsFYzFfwRkrUur6Afu3Fh9M,334
@@ -24,30 +24,33 @@ ripperdoc/cli/commands/tools_cmd.py,sha256=3cMi0vN4mAUhpKqJtRgNvZfcKzRPaMs_pkYYX
24
24
  ripperdoc/cli/ui/__init__.py,sha256=TxSzTYdITlrYmYVfins_w_jzPqqWRpqky5u1ikwvmtM,43
25
25
  ripperdoc/cli/ui/context_display.py,sha256=3ezdtHVwltkPQ5etYwfqUh-fjnpPu8B3P81UzrdHxZs,10020
26
26
  ripperdoc/cli/ui/helpers.py,sha256=TJCipP0neh-96ETQfGhusCJ4aWt5gLw1HZbI-3bWDpw,739
27
- ripperdoc/cli/ui/rich_ui.py,sha256=Qs0rzd-P56Bkd6W7uHXjV2R3pCFAloZOwe2F6fds_L4,51991
27
+ ripperdoc/cli/ui/rich_ui.py,sha256=fGh33f1G-g1oLVxbKz7p8scDX_F3uTl7chFvF2niG2w,53292
28
28
  ripperdoc/cli/ui/spinner.py,sha256=XsPRwJ-70InLX9Qw50CEgSHn5oKA5PFIue8Un4edhUk,1449
29
29
  ripperdoc/cli/ui/thinking_spinner.py,sha256=9Et5EqPChfkmkiOO8w1OPs8t-sHaisgjn9A__kEYLyg,2824
30
30
  ripperdoc/core/__init__.py,sha256=UemJCA-Y8df1466AX-YbRFj071zKajmqO1mi40YVW2g,40
31
- ripperdoc/core/agents.py,sha256=kGVLTcXVwOwrRBvvC9E_tKaQJ5u-wChIYE2VqYMJx00,10348
31
+ ripperdoc/core/agents.py,sha256=Qj9Kr6dx86rStqKGBVrGR8TC3MYhlxc8y2-3JtzeAxs,19629
32
32
  ripperdoc/core/commands.py,sha256=NXCkljYbAP4dDoRy-_3semFNWxG4YAk9q82u8FTKH60,835
33
33
  ripperdoc/core/config.py,sha256=DRPi8uzfBmRKefcqlRKLDf4zYYsj0sxF42ROUZ10GN8,15709
34
- ripperdoc/core/default_tools.py,sha256=t8cLZBOVReF5hvmyhUTziUSnnDMyKDjS7YjuSd_yolw,2568
34
+ ripperdoc/core/default_tools.py,sha256=x5qVT7UL5ISxcmzWO_tp8lFAeB9BlrmKNZKp1D3dVO4,2859
35
35
  ripperdoc/core/permissions.py,sha256=qWaVIaps9Ht0M4jgDk9J0gDErptvf6jzEZ2t48lZ_1I,9187
36
- ripperdoc/core/query.py,sha256=nxi8Q2JH2VHfCsb5FbwQ1k-ZObt2xeAqovsYQVJ9Yjc,29190
37
- ripperdoc/core/query_utils.py,sha256=dmbuFM_PZOntBHBLkyzq0-OubH1z3GF_Nn80aChT_TA,22940
38
- ripperdoc/core/system_prompt.py,sha256=QH7Wg8QqwzvAzjuBayr57w-BazqIQrHfuGvC2KqVkgI,24190
39
- ripperdoc/core/tool.py,sha256=QEmkygAiXsBK3NBnJnL2NevrcgRpG83OIA0TAwuc7OQ,7027
40
- ripperdoc/core/providers/__init__.py,sha256=CSqOeET3UowMQV3esmAHI7JfsyYV-m-C5vC6VutI-fY,888
41
- ripperdoc/core/providers/anthropic.py,sha256=vqQfbGOGKpPEHVc7x4uJFE_nHtWCgiK65KGslVarJkU,5139
42
- ripperdoc/core/providers/base.py,sha256=cwkWUvLzWxesKI-xzvj1STIlMPNgyKGvCBCLIpZgV9c,6604
43
- ripperdoc/core/providers/gemini.py,sha256=aS93WBcLzk871DavETNmS5IYaodQEsVWbS19GhGBYOk,6409
44
- ripperdoc/core/providers/openai.py,sha256=pYMeTQvmnAfT-BimnymDp_7zieNShkfd9573A4S0U7k,5594
36
+ ripperdoc/core/query.py,sha256=mY8DNeDYBUKSLLkDJEu8CiAaHQ7W5G26B-gRvBT9gjE,29513
37
+ ripperdoc/core/query_utils.py,sha256=UEbK2LVQx0ChljtG11yd5Yi8F80JbAxucFgLD-E9-qM,22974
38
+ ripperdoc/core/system_prompt.py,sha256=jbt-yoriW-E15QuiewjM1t0WTMW_MZdbM-ZJ6-5wxV4,26575
39
+ ripperdoc/core/tool.py,sha256=jT1y3cKyQ-KEoNnJ2HfWFDvMn0W8nj8kJ0WIFyNSKKA,7442
40
+ ripperdoc/core/providers/__init__.py,sha256=yevsHF0AUI4b6Wiq_401NXewJ3dqe8LUUtQm0TLPPNQ,1911
41
+ ripperdoc/core/providers/anthropic.py,sha256=m8sRWXrcdqzg6yh2rIZETWXtQQK8dsIfjhDvAORUPzE,5635
42
+ ripperdoc/core/providers/base.py,sha256=f4n7XJEw_B9j0Mlsus4jaBbM74xtYlAbQ1UaVMfQtz4,8218
43
+ ripperdoc/core/providers/gemini.py,sha256=HeAnM5m_DreOVZplc6bfugunDYiN3rAmPzWQG77zvIw,19548
44
+ ripperdoc/core/providers/openai.py,sha256=gAXUs-6l9-750gpdEa5Fp3P6ABrn-NC7hkN5iW4joAI,10760
45
45
  ripperdoc/sdk/__init__.py,sha256=aDSgI4lcCDs9cV3bxNmEEV3SuYx2aCd4VnUjs6H-R7E,213
46
46
  ripperdoc/sdk/client.py,sha256=gkduot4gvN1k41WVkHCpiuU7wGLL-SFG-KTTQrMomWc,10814
47
47
  ripperdoc/tools/__init__.py,sha256=RBFz0DDnztDXMqv_zRxFHVY-ez2HYcncx8zh_y-BX6w,42
48
- ripperdoc/tools/background_shell.py,sha256=1PEdlzOBzRKSOmzO_OViDMXcdXzW9grTzukGzRocYb0,10362
48
+ ripperdoc/tools/ask_user_question_tool.py,sha256=gRMhUzZr3-LABNrBG-o2Necou0i7rZ3eoWJ-RZ035Mg,15373
49
+ ripperdoc/tools/background_shell.py,sha256=8MFYEcjDN3LwT0w_th_7eRjZQokc887DxOmJripgO-M,12419
49
50
  ripperdoc/tools/bash_output_tool.py,sha256=ljIOzTOnkbQfe3jExlhpUlMiLT6HpeD-1QI-D1CwHh8,3379
50
51
  ripperdoc/tools/bash_tool.py,sha256=s4FrJTqC_bIN7gqBtbBUr9gt11LsVmWWkplp1ljlzmw,37461
52
+ ripperdoc/tools/enter_plan_mode_tool.py,sha256=jv7DLSa0GrnXepSblqvVhyTvftVi5qB1nZzjszB7jqc,7950
53
+ ripperdoc/tools/exit_plan_mode_tool.py,sha256=2P270UlOFgKemQxapXRApUoprp-3gMsG__3Tvni7QrA,5702
51
54
  ripperdoc/tools/file_edit_tool.py,sha256=3fLBcXpwX_SumZRiP-1TJ1qIfWA-AQF88Ji_MCI1Ldo,11871
52
55
  ripperdoc/tools/file_read_tool.py,sha256=kKdhj6BXIyKCSxbB063BDAKNjAd36xtTUH-fixWhwHE,6721
53
56
  ripperdoc/tools/file_write_tool.py,sha256=fxh4qUGNqby7iFWuADImF4AyOAskMTzqC7CZLHCqcpo,5296
@@ -55,10 +58,10 @@ ripperdoc/tools/glob_tool.py,sha256=e7yS1RvE3VJw5aOwoEPk8fLUSUr6mUmFsi8u8_3TV9Y,
55
58
  ripperdoc/tools/grep_tool.py,sha256=LfLpPmqD9CIPk6kFYDDmIcykj7uN0xoKtyodc2412FA,8340
56
59
  ripperdoc/tools/kill_bash_tool.py,sha256=36F8w2Rm1IVQitwOAwS-D8NTnyQdWfKWIam44qlXErk,4625
57
60
  ripperdoc/tools/ls_tool.py,sha256=RlaTciHmjgNFopzUEOJjBp1NqNYhr8F1VIekRgqDwSg,15249
58
- ripperdoc/tools/mcp_tools.py,sha256=ejofb2UEwH0r-ZAEvvq9nWvCgRGTAqiOws_X2T0YBU0,31263
61
+ ripperdoc/tools/mcp_tools.py,sha256=aXcCvYdYtslTYbBytfdUDyTvyBrjrzfR_tALGtLUutg,35565
59
62
  ripperdoc/tools/multi_edit_tool.py,sha256=63xircd2ovrk9n1YJ8tfkLDFLvSRFtWAJh3aURmELcE,15829
60
63
  ripperdoc/tools/notebook_edit_tool.py,sha256=zGFLRoIUCYEQUUcAiV4izJ09YFKUjaY2zGUJAjucq8c,12656
61
- ripperdoc/tools/task_tool.py,sha256=bKvJy0hMJcY4MEmWDXqYH0CKfgMKEtaCO9UF9xdngc0,11983
64
+ ripperdoc/tools/task_tool.py,sha256=98U5LjavkTneYgxzqRlYe2rXd9cOPnK_dkS3rL9IpL8,18288
62
65
  ripperdoc/tools/todo_tool.py,sha256=QqgWW7LjylD8nmeeO_rkgjnzs8o1O-UVDD-rXObcVuE,19954
63
66
  ripperdoc/tools/tool_search_tool.py,sha256=YmSGCviaHiMmYghmPO0Km6ZOI5kkQrjz7qb3G_EFjiI,13763
64
67
  ripperdoc/utils/__init__.py,sha256=gdso60znB2hsYZ_YZBKVcuOY3QVfoqD2wHQ4pvr5lSw,37
@@ -69,9 +72,9 @@ ripperdoc/utils/file_watch.py,sha256=idleNFevHctDSJd23d4Y25XdJooFRpgepOcOEmp9pDg
69
72
  ripperdoc/utils/git_utils.py,sha256=Hq-Zx-KPyX4lp_i8ozhic15LyYdX_IfCRm-EyoFu59A,9047
70
73
  ripperdoc/utils/json_utils.py,sha256=Ayv4h0aafTbzhpoN4FMPaBxLx9MfoK2PuOVd1_52-k0,706
71
74
  ripperdoc/utils/log.py,sha256=98JbVkZQd2OsFo789pgf9q96D6A5IcyT5ubzrPAEoWs,6176
72
- ripperdoc/utils/mcp.py,sha256=aIGmJLErQ1xRSzTj9Hm3ns2gW8oTvfrFBIukyNXaURk,17037
75
+ ripperdoc/utils/mcp.py,sha256=DXEG7uNRIl7R-KHZwFMH7VzwGARthrMEJk4viKKVEg4,18284
73
76
  ripperdoc/utils/memory.py,sha256=gRhVTRaCo0YOQeXm1i-sndnTku7qhTB-PSYdFD0OzxU,7872
74
- ripperdoc/utils/message_compaction.py,sha256=307IXnYqGYmj2iZUbfhNULj0BOVSlSTeOUg0Sik79EA,24797
77
+ ripperdoc/utils/message_compaction.py,sha256=aTUogewd337-AW6hG8EL-UGoC11o_PzcDHQ-QzmuSdw,24802
75
78
  ripperdoc/utils/messages.py,sha256=8iRolxS6VoJtcaFU41tzKSdsfBSJMXMGUnUTLXKkfnw,17334
76
79
  ripperdoc/utils/output_utils.py,sha256=R3wqFh9Dko_GK00Exx7XI0DnnldRWMsxZypYX5y6SJo,7448
77
80
  ripperdoc/utils/path_utils.py,sha256=C45Q3OeXnj-0FVEtvf_tdG5922XB6HthUzlUCvfc17Y,1626
@@ -83,13 +86,14 @@ ripperdoc/utils/session_usage.py,sha256=p8_s46zDTzV1qzP4HR4PuZmLeJvSfq9mG_Y5rCnR
83
86
  ripperdoc/utils/shell_token_utils.py,sha256=SduoSU-RERJdM_7gBn0urr5UXtl4XOpPgydBd2fwzWg,2500
84
87
  ripperdoc/utils/shell_utils.py,sha256=t-neFPy_VhEmWZ79J7hh1ULBEdX116Rb9_pnXvir1Jw,5235
85
88
  ripperdoc/utils/todo.py,sha256=ejWd42AAVT15GJvCqb21GERSIbsdY-bwZDNnst4xebQ,6903
89
+ ripperdoc/utils/token_estimation.py,sha256=I_uQOME1P5cs2biSHA0l9KFEcUdXNpfOA4TZsGGR6Os,931
86
90
  ripperdoc/utils/permissions/__init__.py,sha256=-1aKvRC05kuvLacbeu7w1W5ANamOTAho4Y0lY2vz0W0,522
87
91
  ripperdoc/utils/permissions/path_validation_utils.py,sha256=jWcvBWUOgiEOncn_b2Fz7FBJL3XiPlyPQhNT4ZJ7p10,5681
88
92
  ripperdoc/utils/permissions/shell_command_validation.py,sha256=BkK-wEwAuJPoC4XIx2Zj6MF3gXcWReozIwigXgib0z0,2446
89
93
  ripperdoc/utils/permissions/tool_permission_utils.py,sha256=6Fdu9-dMKhLsUExjEjoS0EUeRpEVN5UkqyseIC05YmM,9207
90
- ripperdoc-0.2.3.dist-info/licenses/LICENSE,sha256=bRv9UhBor6GhnQDj12RciDcRfu0R7sB-lqCy1sWF75c,9242
91
- ripperdoc-0.2.3.dist-info/METADATA,sha256=0ko9dUoMiBntF-XdI7oD70VpbU7-GWpDBiZy_oAknq8,5362
92
- ripperdoc-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
93
- ripperdoc-0.2.3.dist-info/entry_points.txt,sha256=79aohFxFPJmrQ3-Mhain04vb3EWpuc0EyzvDDUnwAu4,81
94
- ripperdoc-0.2.3.dist-info/top_level.txt,sha256=u8LbdTr1a-laHgCO0Utl_R3QGFUhLxWelCDnP2ZgpCU,10
95
- ripperdoc-0.2.3.dist-info/RECORD,,
94
+ ripperdoc-0.2.4.dist-info/licenses/LICENSE,sha256=bRv9UhBor6GhnQDj12RciDcRfu0R7sB-lqCy1sWF75c,9242
95
+ ripperdoc-0.2.4.dist-info/METADATA,sha256=r4bibfz7mwDvRftnoPP0UCNaITNjnIZxHP5Xbbs-ulE,5428
96
+ ripperdoc-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
97
+ ripperdoc-0.2.4.dist-info/entry_points.txt,sha256=79aohFxFPJmrQ3-Mhain04vb3EWpuc0EyzvDDUnwAu4,81
98
+ ripperdoc-0.2.4.dist-info/top_level.txt,sha256=u8LbdTr1a-laHgCO0Utl_R3QGFUhLxWelCDnP2ZgpCU,10
99
+ ripperdoc-0.2.4.dist-info/RECORD,,