sirchmunk 0.0.1.post1__py3-none-any.whl → 0.0.2__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 (43) hide show
  1. sirchmunk/api/__init__.py +1 -0
  2. sirchmunk/api/chat.py +1123 -0
  3. sirchmunk/api/components/__init__.py +0 -0
  4. sirchmunk/api/components/history_storage.py +402 -0
  5. sirchmunk/api/components/monitor_tracker.py +518 -0
  6. sirchmunk/api/components/settings_storage.py +353 -0
  7. sirchmunk/api/history.py +254 -0
  8. sirchmunk/api/knowledge.py +411 -0
  9. sirchmunk/api/main.py +120 -0
  10. sirchmunk/api/monitor.py +219 -0
  11. sirchmunk/api/run_server.py +54 -0
  12. sirchmunk/api/search.py +230 -0
  13. sirchmunk/api/settings.py +309 -0
  14. sirchmunk/api/tools.py +315 -0
  15. sirchmunk/cli/__init__.py +11 -0
  16. sirchmunk/cli/cli.py +789 -0
  17. sirchmunk/learnings/knowledge_base.py +5 -2
  18. sirchmunk/llm/prompts.py +12 -1
  19. sirchmunk/retrieve/text_retriever.py +186 -2
  20. sirchmunk/scan/file_scanner.py +2 -2
  21. sirchmunk/schema/knowledge.py +119 -35
  22. sirchmunk/search.py +384 -26
  23. sirchmunk/storage/__init__.py +2 -2
  24. sirchmunk/storage/{knowledge_manager.py → knowledge_storage.py} +265 -60
  25. sirchmunk/utils/constants.py +7 -5
  26. sirchmunk/utils/embedding_util.py +217 -0
  27. sirchmunk/utils/tokenizer_util.py +36 -1
  28. sirchmunk/version.py +1 -1
  29. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/METADATA +124 -9
  30. sirchmunk-0.0.2.dist-info/RECORD +69 -0
  31. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/WHEEL +1 -1
  32. sirchmunk-0.0.2.dist-info/top_level.txt +2 -0
  33. sirchmunk_mcp/__init__.py +25 -0
  34. sirchmunk_mcp/cli.py +478 -0
  35. sirchmunk_mcp/config.py +276 -0
  36. sirchmunk_mcp/server.py +355 -0
  37. sirchmunk_mcp/service.py +327 -0
  38. sirchmunk_mcp/setup.py +15 -0
  39. sirchmunk_mcp/tools.py +410 -0
  40. sirchmunk-0.0.1.post1.dist-info/RECORD +0 -45
  41. sirchmunk-0.0.1.post1.dist-info/top_level.txt +0 -1
  42. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/entry_points.txt +0 -0
  43. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/licenses/LICENSE +0 -0
sirchmunk_mcp/tools.py ADDED
@@ -0,0 +1,410 @@
1
+ # Copyright (c) ModelScope Contributors. All rights reserved.
2
+ """
3
+ MCP Tools definitions for Sirchmunk.
4
+
5
+ Defines the MCP tools that expose Sirchmunk functionality to MCP clients.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ from typing import Any, Dict, List
11
+
12
+ from mcp.types import Tool, TextContent
13
+
14
+ from .service import SirchmunkService
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ # Tool definitions following MCP specification
21
+ SIRCHMUNK_SEARCH_TOOL = Tool(
22
+ name="sirchmunk_search",
23
+ description=(
24
+ "Intelligent code and document search with multi-mode support. "
25
+ "DEEP mode provides comprehensive knowledge extraction with full context analysis. "
26
+ "FAST mode offers quick content search without deep LLM processing. "
27
+ "FILENAME_ONLY mode performs fast filename pattern matching without content search."
28
+ ),
29
+ inputSchema={
30
+ "type": "object",
31
+ "properties": {
32
+ "query": {
33
+ "type": "string",
34
+ "description": "Search query or question (e.g., 'How does authentication work?')",
35
+ },
36
+ "search_paths": {
37
+ "type": "array",
38
+ "items": {"type": "string"},
39
+ "description": "Paths to search in (files or directories)",
40
+ },
41
+ "mode": {
42
+ "type": "string",
43
+ "enum": ["DEEP", "FAST", "FILENAME_ONLY"],
44
+ "default": "DEEP",
45
+ "description": (
46
+ "Search mode: DEEP (comprehensive analysis, 10-30s), "
47
+ "FAST (quick search, 3-8s), FILENAME_ONLY (file discovery, <1s)"
48
+ ),
49
+ },
50
+ "max_depth": {
51
+ "type": "integer",
52
+ "default": 5,
53
+ "minimum": 1,
54
+ "maximum": 20,
55
+ "description": "Maximum directory depth to search",
56
+ },
57
+ "top_k_files": {
58
+ "type": "integer",
59
+ "default": 3,
60
+ "minimum": 1,
61
+ "maximum": 20,
62
+ "description": "Number of top files to return",
63
+ },
64
+ "keyword_levels": {
65
+ "type": "integer",
66
+ "default": 3,
67
+ "minimum": 1,
68
+ "maximum": 5,
69
+ "description": "Keyword granularity levels (DEEP mode only, more levels = more fallback options)",
70
+ },
71
+ "include": {
72
+ "type": "array",
73
+ "items": {"type": "string"},
74
+ "description": "File patterns to include (glob, e.g., ['*.py', '*.md'])",
75
+ },
76
+ "exclude": {
77
+ "type": "array",
78
+ "items": {"type": "string"},
79
+ "description": "File patterns to exclude (glob, e.g., ['*.pyc', '*.log'])",
80
+ },
81
+ "return_cluster": {
82
+ "type": "boolean",
83
+ "default": False,
84
+ "description": "Return full KnowledgeCluster object (DEEP/FAST modes only)",
85
+ },
86
+ },
87
+ "required": ["query", "search_paths"],
88
+ },
89
+ )
90
+
91
+
92
+ SIRCHMUNK_GET_CLUSTER_TOOL = Tool(
93
+ name="sirchmunk_get_cluster",
94
+ description=(
95
+ "Retrieve a previously saved knowledge cluster by its ID. "
96
+ "Knowledge clusters are automatically saved during DEEP mode searches "
97
+ "and contain rich information including evidences, patterns, and constraints."
98
+ ),
99
+ inputSchema={
100
+ "type": "object",
101
+ "properties": {
102
+ "cluster_id": {
103
+ "type": "string",
104
+ "description": "Knowledge cluster ID (e.g., 'C1007')",
105
+ },
106
+ },
107
+ "required": ["cluster_id"],
108
+ },
109
+ )
110
+
111
+
112
+ SIRCHMUNK_LIST_CLUSTERS_TOOL = Tool(
113
+ name="sirchmunk_list_clusters",
114
+ description=(
115
+ "List all saved knowledge clusters with optional filtering and sorting. "
116
+ "Useful for discovering previously searched topics and reusing knowledge."
117
+ ),
118
+ inputSchema={
119
+ "type": "object",
120
+ "properties": {
121
+ "limit": {
122
+ "type": "integer",
123
+ "default": 10,
124
+ "minimum": 1,
125
+ "maximum": 100,
126
+ "description": "Maximum number of clusters to return",
127
+ },
128
+ "sort_by": {
129
+ "type": "string",
130
+ "enum": ["hotness", "confidence", "last_modified"],
131
+ "default": "last_modified",
132
+ "description": (
133
+ "Sort field: hotness (query frequency), "
134
+ "confidence (quality score), last_modified (most recent)"
135
+ ),
136
+ },
137
+ },
138
+ },
139
+ )
140
+
141
+
142
+ # Tool registry
143
+ TOOLS = [
144
+ SIRCHMUNK_SEARCH_TOOL,
145
+ SIRCHMUNK_GET_CLUSTER_TOOL,
146
+ SIRCHMUNK_LIST_CLUSTERS_TOOL,
147
+ ]
148
+
149
+
150
+ async def handle_sirchmunk_search(
151
+ service: SirchmunkService,
152
+ arguments: Dict[str, Any],
153
+ ) -> List[TextContent]:
154
+ """Handle sirchmunk_search tool invocation.
155
+
156
+ Args:
157
+ service: SirchmunkService instance
158
+ arguments: Tool arguments from MCP client
159
+
160
+ Returns:
161
+ List of TextContent with search results
162
+
163
+ Raises:
164
+ ValueError: If required arguments are missing or invalid
165
+ """
166
+ # Extract required arguments
167
+ query = arguments.get("query")
168
+ search_paths = arguments.get("search_paths")
169
+
170
+ if not query:
171
+ raise ValueError("Missing required argument: query")
172
+ if not search_paths:
173
+ raise ValueError("Missing required argument: search_paths")
174
+
175
+ # Extract optional arguments with defaults
176
+ mode = arguments.get("mode", "DEEP")
177
+ max_depth = arguments.get("max_depth")
178
+ top_k_files = arguments.get("top_k_files")
179
+ keyword_levels = arguments.get("keyword_levels")
180
+ include = arguments.get("include")
181
+ exclude = arguments.get("exclude")
182
+ return_cluster = arguments.get("return_cluster", False)
183
+
184
+ logger.info(f"Handling sirchmunk_search: mode={mode}, query='{query[:50]}...'")
185
+
186
+ try:
187
+ # Perform search
188
+ result = await service.search(
189
+ query=query,
190
+ search_paths=search_paths,
191
+ mode=mode,
192
+ max_depth=max_depth,
193
+ top_k_files=top_k_files,
194
+ keyword_levels=keyword_levels,
195
+ include=include,
196
+ exclude=exclude,
197
+ return_cluster=return_cluster,
198
+ )
199
+
200
+ # Format response based on result type
201
+ if result is None:
202
+ response_text = f"No results found for query: {query}"
203
+
204
+ elif isinstance(result, str):
205
+ # DEEP or FAST mode: string summary
206
+ response_text = result
207
+
208
+ elif isinstance(result, list):
209
+ # FILENAME_ONLY mode: list of file matches
210
+ response_text = _format_filename_results(result, query)
211
+
212
+ elif hasattr(result, "to_dict"):
213
+ # KnowledgeCluster object
214
+ response_text = _format_cluster(result)
215
+
216
+ else:
217
+ response_text = str(result)
218
+
219
+ return [TextContent(
220
+ type="text",
221
+ text=response_text,
222
+ )]
223
+
224
+ except Exception as e:
225
+ logger.error(f"Search failed: {e}", exc_info=True)
226
+ error_message = f"Search failed: {str(e)}"
227
+ return [TextContent(
228
+ type="text",
229
+ text=error_message,
230
+ )]
231
+
232
+
233
+ async def handle_sirchmunk_get_cluster(
234
+ service: SirchmunkService,
235
+ arguments: Dict[str, Any],
236
+ ) -> List[TextContent]:
237
+ """Handle sirchmunk_get_cluster tool invocation.
238
+
239
+ Args:
240
+ service: SirchmunkService instance
241
+ arguments: Tool arguments from MCP client
242
+
243
+ Returns:
244
+ List of TextContent with cluster information
245
+
246
+ Raises:
247
+ ValueError: If required arguments are missing
248
+ """
249
+ cluster_id = arguments.get("cluster_id")
250
+
251
+ if not cluster_id:
252
+ raise ValueError("Missing required argument: cluster_id")
253
+
254
+ logger.info(f"Handling sirchmunk_get_cluster: cluster_id={cluster_id}")
255
+
256
+ try:
257
+ cluster = await service.get_cluster(cluster_id)
258
+
259
+ if cluster is None:
260
+ response_text = f"Cluster not found: {cluster_id}"
261
+ else:
262
+ response_text = _format_cluster(cluster)
263
+
264
+ return [TextContent(
265
+ type="text",
266
+ text=response_text,
267
+ )]
268
+
269
+ except Exception as e:
270
+ logger.error(f"Get cluster failed: {e}", exc_info=True)
271
+ error_message = f"Failed to retrieve cluster: {str(e)}"
272
+ return [TextContent(
273
+ type="text",
274
+ text=error_message,
275
+ )]
276
+
277
+
278
+ async def handle_sirchmunk_list_clusters(
279
+ service: SirchmunkService,
280
+ arguments: Dict[str, Any],
281
+ ) -> List[TextContent]:
282
+ """Handle sirchmunk_list_clusters tool invocation.
283
+
284
+ Args:
285
+ service: SirchmunkService instance
286
+ arguments: Tool arguments from MCP client
287
+
288
+ Returns:
289
+ List of TextContent with cluster listing
290
+ """
291
+ limit = arguments.get("limit", 10)
292
+ sort_by = arguments.get("sort_by", "last_modified")
293
+
294
+ logger.info(f"Handling sirchmunk_list_clusters: limit={limit}, sort_by={sort_by}")
295
+
296
+ try:
297
+ clusters = await service.list_clusters(limit=limit, sort_by=sort_by)
298
+
299
+ if not clusters:
300
+ response_text = "No knowledge clusters found."
301
+ else:
302
+ response_text = _format_cluster_list(clusters, sort_by)
303
+
304
+ return [TextContent(
305
+ type="text",
306
+ text=response_text,
307
+ )]
308
+
309
+ except Exception as e:
310
+ logger.error(f"List clusters failed: {e}", exc_info=True)
311
+ error_message = f"Failed to list clusters: {str(e)}"
312
+ return [TextContent(
313
+ type="text",
314
+ text=error_message,
315
+ )]
316
+
317
+
318
+ def _format_filename_results(results: List[Dict[str, Any]], query: str) -> str:
319
+ """Format FILENAME_ONLY mode results.
320
+
321
+ Args:
322
+ results: List of filename match dictionaries
323
+ query: Original query
324
+
325
+ Returns:
326
+ Formatted string representation
327
+ """
328
+ lines = [
329
+ f"# Filename Search Results",
330
+ f"",
331
+ f"**Query**: `{query}`",
332
+ f"**Found**: {len(results)} matching file(s)",
333
+ f"",
334
+ ]
335
+
336
+ for i, result in enumerate(results, 1):
337
+ lines.append(f"## {i}. {result['filename']}")
338
+ lines.append(f"- **Path**: `{result['path']}`")
339
+ lines.append(f"- **Relevance**: {result['match_score']:.2f}")
340
+ if "matched_pattern" in result:
341
+ lines.append(f"- **Pattern**: `{result['matched_pattern']}`")
342
+ lines.append("")
343
+
344
+ return "\n".join(lines)
345
+
346
+
347
+ def _format_cluster(cluster: Any) -> str:
348
+ """Format KnowledgeCluster object.
349
+
350
+ Args:
351
+ cluster: KnowledgeCluster object
352
+
353
+ Returns:
354
+ Formatted string representation
355
+ """
356
+ # Use the cluster's __str__ method for human-readable format
357
+ return str(cluster)
358
+
359
+
360
+ def _format_cluster_list(clusters: List[Dict[str, Any]], sort_by: str) -> str:
361
+ """Format cluster list.
362
+
363
+ Args:
364
+ clusters: List of cluster metadata dictionaries
365
+ sort_by: Sort field used
366
+
367
+ Returns:
368
+ Formatted string representation
369
+ """
370
+ lines = [
371
+ f"# Knowledge Clusters",
372
+ f"",
373
+ f"**Total**: {len(clusters)} cluster(s)",
374
+ f"**Sorted by**: {sort_by}",
375
+ f"",
376
+ ]
377
+
378
+ for i, cluster in enumerate(clusters, 1):
379
+ lines.append(f"## {i}. {cluster['name']}")
380
+ lines.append(f"- **ID**: `{cluster['id']}`")
381
+ lines.append(f"- **Lifecycle**: {cluster['lifecycle']}")
382
+ lines.append(f"- **Version**: {cluster['version']}")
383
+
384
+ if cluster['confidence'] is not None:
385
+ lines.append(f"- **Confidence**: {cluster['confidence']:.2f}")
386
+
387
+ if cluster['hotness'] is not None:
388
+ lines.append(f"- **Hotness**: {cluster['hotness']:.2f}")
389
+
390
+ if cluster['last_modified']:
391
+ lines.append(f"- **Last Modified**: {cluster['last_modified']}")
392
+
393
+ if cluster['queries']:
394
+ queries_preview = ", ".join(f'"{q}"' for q in cluster['queries'][:3])
395
+ if len(cluster['queries']) > 3:
396
+ queries_preview += f" (+{len(cluster['queries']) - 3} more)"
397
+ lines.append(f"- **Related Queries**: {queries_preview}")
398
+
399
+ lines.append(f"- **Evidences**: {cluster['evidences_count']}")
400
+ lines.append("")
401
+
402
+ return "\n".join(lines)
403
+
404
+
405
+ # Tool handler registry
406
+ TOOL_HANDLERS = {
407
+ "sirchmunk_search": handle_sirchmunk_search,
408
+ "sirchmunk_get_cluster": handle_sirchmunk_get_cluster,
409
+ "sirchmunk_list_clusters": handle_sirchmunk_list_clusters,
410
+ }
@@ -1,45 +0,0 @@
1
- sirchmunk/__init__.py,sha256=5sdppELUcjnTofJtwZ2ACuUscmLYIPhCwj-G-MMSl-M,184
2
- sirchmunk/base.py,sha256=qVQ63QfEWhEvOJl3OxQvC2rOUNTZCD5weXRn-1vvEkU,439
3
- sirchmunk/search.py,sha256=NROpV39oklDT4wusE8EnkQPABq9LiAa0kQrtyR6viHQ,18210
4
- sirchmunk/version.py,sha256=kBLMG2WZUGcfsN5po-4OyjkyndbcTcGgae_91l8-sFE,28
5
- sirchmunk/insight/__init__.py,sha256=7sQeT0fSg5-b9jrwthA9_fSCR1Q5qSvm33L9l7kHjLY,144
6
- sirchmunk/insight/text_insights.py,sha256=BwFqmDNG8FmOz4Lv3qO1Mz2xRv_LVg4b3wj6YUhVLVE,9075
7
- sirchmunk/learnings/__init__.py,sha256=310L84MdAIw4THnzf5YsLiUhW_oaxgJHHcZZeMso3jY,61
8
- sirchmunk/learnings/evidence_processor.py,sha256=QDg-qReSte8R8I2BrRRC-d-Glyfjcnqazbe57-PSDHU,17922
9
- sirchmunk/learnings/knowledge_base.py,sha256=8szliKLFKu_BTIVf0vhy2mSe7Tix4145Umdd5OQ2rww,8343
10
- sirchmunk/llm/__init__.py,sha256=4ynF6R63afMWW1d9T21C8JqfVc9xJTiOFXZNzDwPLww,98
11
- sirchmunk/llm/openai_chat.py,sha256=ET7HqEoFTbbvhUTlAUZNOoJwxF9hA73qeH3xFTsNK-w,8138
12
- sirchmunk/llm/prompts.py,sha256=8Wpugif43EhCaDuRF4U6xRgbv6EB_UdU-2NdDFuglM0,8755
13
- sirchmunk/retrieve/__init__.py,sha256=310L84MdAIw4THnzf5YsLiUhW_oaxgJHHcZZeMso3jY,61
14
- sirchmunk/retrieve/base.py,sha256=VDpCwdwhjVYuj0mbe78qg_FhbcQkasbCS0cd66hQ4hk,618
15
- sirchmunk/retrieve/text_retriever.py,sha256=1ryDjqecXJz2bwjeoJhaUvMxCAdoNzPMMOAJYH3x7g0,39484
16
- sirchmunk/scan/__init__.py,sha256=310L84MdAIw4THnzf5YsLiUhW_oaxgJHHcZZeMso3jY,61
17
- sirchmunk/scan/base.py,sha256=3Jqvn0W9KJ00u2oy8U-qD16KksV7taAx4s9qhCpRD-c,496
18
- sirchmunk/scan/file_scanner.py,sha256=GtGFP9ZwqemOSkHjFcEDaD2WCnw5qMB_bCeiVUaIEQg,14645
19
- sirchmunk/scan/web_scanner.py,sha256=YQRBQ5JnATwNdek2o1_Y8GB0eG0IWQvx4YhqW4Zc5Tw,531
20
- sirchmunk/scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- sirchmunk/schema/__init__.py,sha256=kvzKU8Rt1t91Rm5tuy6KqgZXj8lfYQ2q1XvLYGNLUh4,103
22
- sirchmunk/schema/cognition.py,sha256=NDgCOjMpqyUBxd_ymeMQrjSr3Ri-nJeMUNRuWfLUhBI,4031
23
- sirchmunk/schema/context.py,sha256=wwsSBE5EJXmrvh-RCluf3nJm7hcxkWaiNWfJ10coGY4,628
24
- sirchmunk/schema/knowledge.py,sha256=mPKuhqXzLFIfcz5yHRcNe7B4WifUVlHDNnzeevnFKQM,11108
25
- sirchmunk/schema/metadata.py,sha256=VdUD6GPCB_jtyBkowkGEpCPMYL1c-sFVSUloKPSZRwE,22810
26
- sirchmunk/schema/request.py,sha256=EPum-IzmN15EgFmwN12oL-CNoI-KvTcHN4nLuO-c09c,7702
27
- sirchmunk/schema/response.py,sha256=6xc5tvAnqL_zpUhQtchVGwbrt1btzy4QArCeS81DrIU,488
28
- sirchmunk/schema/snapshot.py,sha256=zZSKDRN8jMtpOIH3TL0FCrAn7AlUc_5zO8qFX2f2u_s,12341
29
- sirchmunk/storage/__init__.py,sha256=9mF1JdbVrgC0mt9uvBYU9RwiLGRdrmwGAyTj7EA21Pg,231
30
- sirchmunk/storage/duckdb.py,sha256=Jw_EK9i589YyKXYhi-yjAmX7zIM7txDsoxy0Gx-XY8o,23092
31
- sirchmunk/storage/knowledge_manager.py,sha256=YYlZa4CnbCXzFWjOleztil2hohqrvrOZs-8UcV0DTEM,27746
32
- sirchmunk/utils/__init__.py,sha256=33bVrhpUfPXpM85r_SEB07QJnGDjD-4BE5p9vpF-fXw,265
33
- sirchmunk/utils/constants.py,sha256=D6RP2mtRH9uA3ZI1SpFD77_QPtYkxIKIi7zZY8-do1w,551
34
- sirchmunk/utils/deps.py,sha256=QTL0k7CN1t0_r-CrZ4TM__pdK8U2X1d0OKvJUtQe_xA,563
35
- sirchmunk/utils/file_utils.py,sha256=9OtYNffXbo1Pz6XuJsOECQS_mYRBg1NpGUsglNgfWnU,2257
36
- sirchmunk/utils/install_rga.py,sha256=i7sWYi6u2A32dc0mq5LB_OtqcMWqKezSz6WILWaK1Oc,5215
37
- sirchmunk/utils/log_utils.py,sha256=HujosgEXV9fpSVd6JjRh7KEQuskhrcpC4Kit6aIpnW0,14195
38
- sirchmunk/utils/tokenizer_util.py,sha256=JIg4FylB6o8AIe71q8Uv83PKmPF1funX3ffhb9cPX3c,1624
39
- sirchmunk/utils/utils.py,sha256=qnwZ8R9xLqsMooW0IdcWoPKto7q4AQ49-SOR33rCy1g,3394
40
- sirchmunk-0.0.1.post1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
41
- sirchmunk-0.0.1.post1.dist-info/METADATA,sha256=H4zSxLJBlsFVfMPT1HscJvNkCfR0AmfNEA0m0XfQsoQ,18408
42
- sirchmunk-0.0.1.post1.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
43
- sirchmunk-0.0.1.post1.dist-info/entry_points.txt,sha256=lpnP-Ll2CUY0P1wYm6kutcBMrxwG67astmgY-vVhF14,56
44
- sirchmunk-0.0.1.post1.dist-info/top_level.txt,sha256=8MiQvqjFkqiGJ7m4xqxsfxwxFHtH2mIXlrkV9PfX-aM,10
45
- sirchmunk-0.0.1.post1.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- sirchmunk