├── .python-version ├── main.py ├── .gitignore ├── pyproject.toml ├── type_definitions.py ├── codex └── codex-mcp.py ├── README.md ├── LICENSE.txt ├── coderank.py ├── github_tools.py └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from server import mcp 2 | 3 | 4 | if __name__ == "__main__": 5 | mcp.run(transport="stdio") 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | __pycache__/ 3 | *.py[oc] 4 | build/ 5 | dist/ 6 | wheels/ 7 | *.egg-info 8 | 9 | # Virtual environments 10 | .venv 11 | .venv 12 | **/__pycache__ 13 | **/.DS_Store 14 | REFERENCE.md 15 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "search-tools" 3 | version = "0.1.0" 4 | description = "Add your description here" 5 | readme = "README.md" 6 | requires-python = ">=3.13" 7 | dependencies = [ 8 | "mcp[cli]>=1.9.4", 9 | "cased-kit", 10 | "networkx>=3.5", 11 | "scipy>=1.16.0", 12 | "openai-agents>=0.2.0", 13 | ] 14 | -------------------------------------------------------------------------------- /type_definitions.py: -------------------------------------------------------------------------------- 1 | from typing import List, Set, TypedDict 2 | 3 | 4 | # Type definitions for better type safety 5 | class ModuleChangeData(TypedDict): 6 | commits: List[str] 7 | authors: Set[str] 8 | commit_count: int 9 | lines_changed: int 10 | coderank_score: float 11 | files: Set[str] 12 | 13 | class CochangeData(TypedDict): 14 | count: int 15 | commits: List[str] 16 | authors: Set[str] 17 | combined_coderank: float 18 | 19 | class ContributorData(TypedDict): 20 | commits: int 21 | modules_touched: Set[str] 22 | impact_score: float 23 | lines_added: int 24 | lines_removed: int 25 | important_module_commits: int 26 | recent_commits: List[str] 27 | 28 | class PropagationData(TypedDict): 29 | co_change_count: int 30 | commits: List[str] 31 | is_import_related: bool 32 | is_test: bool 33 | module_score: float 34 | 35 | 36 | def create_module_change_data() -> ModuleChangeData: 37 | """Factory function for module change data""" 38 | return { 39 | 'commits': [], 40 | 'authors': set(), 41 | 'commit_count': 0, 42 | 'lines_changed': 0, 43 | 'coderank_score': 0, 44 | 'files': set() 45 | } 46 | 47 | def create_cochange_data() -> CochangeData: 48 | """Factory function for cochange data""" 49 | return { 50 | 'count': 0, 51 | 'commits': [], 52 | 'authors': set(), 53 | 'combined_coderank': 0 54 | } 55 | 56 | def create_contributor_data() -> ContributorData: 57 | """Factory function for contributor data""" 58 | return { 59 | 'commits': 0, 60 | 'modules_touched': set(), 61 | 'impact_score': 0, 62 | 'lines_added': 0, 63 | 'lines_removed': 0, 64 | 'important_module_commits': 0, 65 | 'recent_commits': [] 66 | } 67 | 68 | def create_propagation_data() -> PropagationData: 69 | """Factory function for propagation data""" 70 | return { 71 | 'co_change_count': 0, 72 | 'commits': [], 73 | 'is_import_related': False, 74 | 'is_test': False, 75 | 'module_score': 0 76 | } 77 | -------------------------------------------------------------------------------- /codex/codex-mcp.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | import asyncio 4 | from agents import Agent, Runner, set_default_openai_api 5 | from agents.mcp import MCPServerStdio 6 | 7 | load_dotenv(override=True) # load the API key from the .env file. We set override to True here to ensure the notebook is loading any changes 8 | #set_default_openai_api(os.getenv("OPENAI_API_KEY")) 9 | 10 | async def main() -> None: 11 | async with MCPServerStdio( 12 | name="Codex CLI", 13 | params={ 14 | "command": "npx", 15 | "args": ["-y", "codex", "mcp-server"], 16 | "env": dict(os.environ), 17 | }, 18 | client_session_timeout_seconds=360000, 19 | ) as codex_mcp_server: 20 | developer_agent = Agent( 21 | name="Game Developer", 22 | instructions=( 23 | "You are an expert in building simple games using basic html + css + javascript with no dependencies. " 24 | "Save your work in a file called index.html in the current directory." 25 | "Always call codex with \"approval-policy\": \"never\" and \"sandbox\": \"workspace-write\"" 26 | ), 27 | mcp_servers=[codex_mcp_server], 28 | ) 29 | 30 | designer_agent = Agent( 31 | name="Game Designer", 32 | instructions=( 33 | "You are an indie game connoisseur. Come up with an idea for a single page html + css + javascript game that a developer could build in about 50 lines of code. " 34 | "Format your request as a 3 sentence design brief for a game developer and call the Game Developer coder with your idea." 35 | ), 36 | model="gpt-5", 37 | handoffs=[developer_agent], 38 | ) 39 | 40 | result = await Runner.run(designer_agent, "Implement a fun new game!") 41 | # print(result.final_output) 42 | 43 | 44 | if __name__ == "__main__": 45 | # Jupyter/IPython already runs an event loop, so calling asyncio.run() here 46 | # raises "asyncio.run() cannot be called from a running event loop". 47 | # Workaround: if a loop is running (notebook), schedule the coroutine on that loop; 48 | # otherwise use asyncio.run(). 49 | try: 50 | loop = asyncio.get_running_loop() 51 | except RuntimeError: 52 | asyncio.run(main()) 53 | else: 54 | loop.create_task(main()) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🔍 Search Tools MCP Server 2 | 3 | > ⚡ An intelligent Model Context Protocol (MCP) server that supercharges code analysis with advanced search capabilities and dependency mapping 4 | 5 | ## 🌟 Overview 6 | 7 | The **Search Tools MCP Server** is a powerful toolkit that combines traditional code search with intelligent analysis algorithms. It leverages the **CodeRank** algorithm (inspired by PageRank) to identify the most critical modules in your codebase and provides sophisticated search capabilities that go beyond simple text matching. 8 | 9 | ## 🎯 Key Features 10 | 11 | ### 🔎 **Smart Search Capabilities** 12 | - **Contextual Keyword Search**: Ripgrep-powered search with configurable context lines 13 | - **Symbol Discovery**: Extract and analyze functions, classes, methods, and modules 14 | - **Usage Tracking**: Find where symbols are used across your codebase 15 | - **Priority-Ranked Results**: Search results ranked by code importance 16 | 17 | ### 🧠 **Intelligence & Analysis** 18 | - **CodeRank Algorithm**: Identify the most critical modules using network analysis 19 | - **Dependency Mapping**: Trace complex dependency chains and impact analysis 20 | - **Hotspot Detection**: Find code areas that are both highly connected and frequently used 21 | - **Refactoring Impact**: Analyze the potential impact of code changes 22 | 23 | ### 🎨 **Advanced Filtering** 24 | - Symbol type filtering (functions, methods, classes) 25 | - File inclusion/exclusion patterns 26 | - External module dependency tracking 27 | - Markdown documentation analysis 28 | 29 | ## 🛠️ Installation 30 | 31 | ### Prerequisites 32 | - Python 3.13+ 33 | - `uv` package manager 34 | - `kit` CLI tool (for symbol analysis) 35 | - `ripgrep` (for fast text search) 36 | 37 | ### Setup 38 | ```bash 39 | # Clone the repository 40 | git clone 41 | cd search-tools 42 | 43 | # Install dependencies 44 | uv sync 45 | ``` 46 | 47 | ## ⚙️ Configuration 48 | 49 | ### Adding to Cursor/Windsurf 50 | 51 | Add the following configuration to your `mcp.json` file: 52 | 53 | ```json 54 | { 55 | "mcpServers": { 56 | "search-tools": { 57 | "command": "/path/to/uv", 58 | "args": [ 59 | "run", 60 | "--directory", 61 | "/path/to/search-tools", 62 | "main.py" 63 | ] 64 | } 65 | } 66 | } 67 | ``` 68 | 69 | **For macOS users with Homebrew:** 70 | ```json 71 | { 72 | "mcpServers": { 73 | "search-tools": { 74 | "command": "/Users/yourusername/.local/bin/uv", 75 | "args": [ 76 | "run", 77 | "--directory", 78 | "/path/to/your/search-tools/directory", 79 | "main.py" 80 | ] 81 | } 82 | } 83 | } 84 | ``` 85 | 86 | ### To add to claude code: 87 | ```bash 88 | claude mcp add-json search-tools '{"type":"stdio","command":"/Users/yourusername/.local/bin/uv","args":[ "run", "--directory", "/path/to/your/search-tools/directory", "main.py"]}' 89 | ``` 90 | 91 | ### 📍 Finding Your Paths 92 | 93 | To find the correct paths for your system: 94 | 95 | ```bash 96 | # Find uv location 97 | which uv 98 | 99 | # Get absolute path to search-tools directory 100 | pwd # (run this from the search-tools directory) 101 | ``` 102 | 103 | ## 🚀 Available Tools 104 | 105 | ### 🔍 `contextual_keyword_search` 106 | Search for keywords with configurable context lines around matches. 107 | 108 | **Parameters:** 109 | - `keyword`: Search term (case insensitive) 110 | - `working_directory`: Absolute path to search directory 111 | - `num_context_lines`: Lines of context (default: 2) 112 | 113 | ### 🏗️ `get_repo_symbols` 114 | Extract symbols (functions, classes, methods) from your codebase. 115 | 116 | **Parameters:** 117 | - `repo`: Repository path 118 | - `working_directory`: Command execution directory 119 | - `keep_types`: Filter by symbol types 120 | - `file_must_contain/file_must_not_contain`: File filtering 121 | 122 | ### 📊 `get_symbol_usages` 123 | Find where specific symbols are used throughout your codebase. 124 | 125 | **Parameters:** 126 | - `repo`: Repository path 127 | - `symbol_name_or_substring`: Symbol to search for 128 | - `working_directory`: Command execution directory 129 | - `symbol_type`: Optional type filter 130 | 131 | ### 🎯 `coderank_analysis` 132 | Analyze repository importance using the CodeRank algorithm. 133 | 134 | **Parameters:** 135 | - `repo_path`: Repository to analyze 136 | - `external_modules`: Comma-separated external dependencies 137 | - `top_n`: Number of top modules to return (default: 10) 138 | - `analyze_markdown`: Include markdown files 139 | - `output_format`: "summary", "detailed", or "json" 140 | 141 | ### 🔥 `find_code_hotspots` 142 | Identify critical code areas combining connectivity and usage frequency. 143 | 144 | **Parameters:** 145 | - `repo_path`: Repository path 146 | - `working_directory`: Command execution directory 147 | - `min_connections`: Minimum import connections (default: 5) 148 | - `include_external`: Include external dependencies 149 | - `top_n`: Number of hotspots to return (default: 20) 150 | 151 | ### 🌐 `trace_dependency_impact` 152 | Trace dependency chains and analyze refactoring impact. 153 | 154 | **Parameters:** 155 | - `repo_path`: Repository path 156 | - `target_module`: Module to analyze 157 | - `working_directory`: Command execution directory 158 | - `analysis_type`: "dependency", "refactoring", or "both" 159 | - `max_depth`: Maximum trace depth (default: 3) 160 | - `change_type`: "modify", "split", "merge", or "remove" 161 | 162 | ### 🎪 `smart_code_search` 163 | Enhanced search combining ripgrep with CodeRank prioritization. 164 | 165 | **Parameters:** 166 | - `keyword`: Search term (supports regex) 167 | - `repo_path`: Repository path 168 | - `working_directory`: Command execution directory 169 | - `rank_results`: Sort by module importance 170 | - `context_lines`: Context lines around matches (default: 3) 171 | - `max_results`: Maximum results to return (default: 20) 172 | 173 | ## 🧪 Development & Testing 174 | 175 | ### Running the Server 176 | ```bash 177 | # Development mode 178 | uv run mcp dev main.py 179 | 180 | # Testing with MCP Inspector 181 | npx @modelcontextprotocol/inspector python main.py 182 | ``` 183 | 184 | ### 🔧 Dependencies 185 | - **mcp[cli]**: Model Context Protocol framework 186 | - **cased-kit**: Symbol analysis toolkit 187 | - **networkx**: Graph analysis for CodeRank algorithm 188 | 189 | ## 🎨 Algorithm Details 190 | 191 | ### CodeRank Algorithm 192 | The CodeRank algorithm treats your codebase as a directed graph where: 193 | - **Nodes**: Python modules, classes, functions, methods 194 | - **Edges**: Import relationships and dependencies 195 | - **Weights**: Different weights for internal vs external dependencies 196 | 197 | This creates a ranking system that identifies the most "central" and important parts of your codebase, similar to how PageRank identifies important web pages. 198 | 199 | ## 💡 Use Cases 200 | 201 | - **🔍 Code Exploration**: Quickly understand large codebases 202 | - **🏗️ Refactoring Planning**: Identify high-impact areas before changes 203 | - **📚 Documentation**: Find the most important modules to document first 204 | - **🐛 Bug Investigation**: Focus on critical code paths 205 | - **👥 Code Review**: Prioritize review efforts on important modules 206 | 207 | ## 🤝 Contributing 208 | 209 | Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests. 210 | 211 | ## 📄 License 212 | 213 | This project is open source. Please check the license file for details. 214 | 215 | --- 216 | 217 |
218 | 219 | **🔮 Powered by the CodeRank Algorithm & Model Context Protocol** 220 | 221 | *Making code search intelligent, one repository at a time* 222 | 223 |
224 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /coderank.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import os 3 | import re 4 | from collections import defaultdict 5 | from pathlib import Path 6 | from typing import Dict, List, Set, Tuple, Optional 7 | import networkx as nx 8 | 9 | 10 | def path_to_module_fqn(file_path: str, abs_repo_path: str) -> Optional[str]: 11 | """ 12 | Converts an absolute file path to a fully qualified Python module name. 13 | e.g., /path/to/repo/pkg/mod.py -> pkg.mod 14 | e.g., /path/to/repo/pkg/__init__.py -> pkg 15 | """ 16 | file_path = os.path.normpath(os.path.abspath(file_path)) 17 | abs_repo_path = os.path.normpath(os.path.abspath(abs_repo_path)) 18 | 19 | if not file_path.startswith(abs_repo_path): 20 | return None 21 | 22 | repo_path_for_rel = abs_repo_path 23 | if abs_repo_path != os.path.dirname(abs_repo_path): 24 | repo_path_for_rel = abs_repo_path + os.path.sep 25 | 26 | try: 27 | relative_path = os.path.relpath(file_path, abs_repo_path) 28 | except ValueError: 29 | return None 30 | 31 | module_path_no_ext, _ = os.path.splitext(relative_path) 32 | parts = module_path_no_ext.split(os.path.sep) 33 | 34 | if not parts: 35 | return None 36 | 37 | if parts[-1] == "__init__": 38 | parts.pop() 39 | if not parts: 40 | return None 41 | 42 | return ".".join(parts) if parts else None 43 | 44 | 45 | def resolve_relative_import(current_module_fqn: str, level: int, module_in_from_statement: Optional[str]) -> Optional[str]: 46 | """ 47 | Resolves a relative import to a fully qualified name. 48 | """ 49 | if not current_module_fqn: 50 | if level > 0 and module_in_from_statement: 51 | return module_in_from_statement 52 | return None 53 | 54 | path_parts = current_module_fqn.split('.') 55 | 56 | if level > len(path_parts): 57 | return None 58 | 59 | base_module_parts = path_parts[:-level] 60 | 61 | if module_in_from_statement: 62 | resolved_parts = base_module_parts + module_in_from_statement.split('.') 63 | return ".".join(resolved_parts) 64 | else: 65 | if not base_module_parts: 66 | return None 67 | return ".".join(base_module_parts) 68 | 69 | 70 | def get_imports_from_file(file_path: str, current_module_fqn: str) -> Set[str]: 71 | """ 72 | Parses a Python file and extracts its imports as fully qualified names. 73 | """ 74 | imports = set() 75 | try: 76 | with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: 77 | content = f.read() 78 | tree = ast.parse(content, filename=file_path) 79 | except Exception: 80 | return imports 81 | 82 | for node in ast.walk(tree): 83 | if isinstance(node, ast.Import): 84 | for alias in node.names: 85 | imports.add(alias.name) 86 | 87 | elif isinstance(node, ast.ImportFrom): 88 | module_name_in_from = node.module 89 | level = node.level 90 | 91 | if level > 0: # Relative import 92 | resolved_base = resolve_relative_import(current_module_fqn, level, module_name_in_from) 93 | if not resolved_base: 94 | continue 95 | 96 | if module_name_in_from: 97 | imports.add(resolved_base) 98 | else: 99 | for alias in node.names: 100 | imports.add(f"{resolved_base}.{alias.name}") 101 | 102 | else: # Absolute import 103 | if module_name_in_from: 104 | imports.add(module_name_in_from) 105 | return imports 106 | 107 | 108 | def extract_python_symbols(file_path: str, current_module_fqn: str) -> Dict[str, Dict[str, str]]: 109 | """ 110 | Extracts modules, classes, functions, and methods FQNs from a Python file. 111 | Returns a dict of symbol_fqn -> {type, module_fqn, file_path} 112 | """ 113 | symbols = {} 114 | 115 | if not current_module_fqn: 116 | return symbols 117 | 118 | try: 119 | with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: 120 | content = f.read() 121 | tree = ast.parse(content, filename=file_path) 122 | except Exception: 123 | return symbols 124 | 125 | # Add module symbol itself 126 | symbols[current_module_fqn] = { 127 | "type": "module", 128 | "module_fqn": current_module_fqn, 129 | "file_path": file_path, 130 | } 131 | 132 | # Process top-level items 133 | for node in tree.body: 134 | if isinstance(node, ast.ClassDef): 135 | class_fqn = f"{current_module_fqn}.{node.name}" 136 | symbols[class_fqn] = { 137 | "type": "class", 138 | "module_fqn": current_module_fqn, 139 | "file_path": file_path, 140 | } 141 | # Extract methods within this class 142 | for sub_node in node.body: 143 | if isinstance(sub_node, (ast.FunctionDef, ast.AsyncFunctionDef)): 144 | method_fqn = f"{class_fqn}.{sub_node.name}" 145 | symbols[method_fqn] = { 146 | "type": "method", 147 | "module_fqn": current_module_fqn, 148 | "file_path": file_path, 149 | } 150 | elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): 151 | function_fqn = f"{current_module_fqn}.{node.name}" 152 | symbols[function_fqn] = { 153 | "type": "function", 154 | "module_fqn": current_module_fqn, 155 | "file_path": file_path, 156 | } 157 | 158 | return symbols 159 | 160 | 161 | def discover_python_files(repo_path: str) -> List[str]: 162 | """Finds all .py files in the repository.""" 163 | py_files = [] 164 | abs_repo_path = os.path.abspath(repo_path) 165 | for root, _, files in os.walk(abs_repo_path): 166 | for file in files: 167 | if file.endswith(".py"): 168 | py_files.append(os.path.join(root, file)) 169 | return py_files 170 | 171 | 172 | def discover_markdown_files(repo_path: str) -> List[str]: 173 | """Finds all .md and .markdown files in the repository.""" 174 | md_files = [] 175 | abs_repo_path = os.path.abspath(repo_path) 176 | for root, _, files in os.walk(abs_repo_path): 177 | for file in files: 178 | if file.endswith(".md") or file.endswith(".markdown"): 179 | md_files.append(os.path.join(root, file)) 180 | return md_files 181 | 182 | 183 | def analyze_markdown_file_references(md_file_path: str, all_python_fqns: Set[str], python_symbols_db: Dict) -> Set[str]: 184 | """ 185 | Analyzes a Markdown file to find references to Python symbols. 186 | Returns a set of module FQNs that are referenced in the Markdown file. 187 | """ 188 | referenced_module_fqns_in_md = set() 189 | try: 190 | with open(md_file_path, 'r', encoding='utf-8', errors='ignore') as f: 191 | content = f.read() 192 | except Exception: 193 | return referenced_module_fqns_in_md 194 | 195 | for py_fqn in all_python_fqns: 196 | escaped_py_fqn = re.escape(py_fqn) 197 | try: 198 | if re.search(r'\b' + escaped_py_fqn + r'\b', content): 199 | symbol_info = python_symbols_db.get(py_fqn) 200 | if symbol_info and "module_fqn" in symbol_info: 201 | referenced_module_fqns_in_md.add(symbol_info["module_fqn"]) 202 | elif py_fqn in python_symbols_db and python_symbols_db[py_fqn]["type"] == "module": 203 | referenced_module_fqns_in_md.add(py_fqn) 204 | except re.error: 205 | pass 206 | 207 | return referenced_module_fqns_in_md 208 | 209 | 210 | def calculate_coderank( 211 | repo_path: str, 212 | external_modules: List[str] = None, 213 | damping_factor: float = 0.85, 214 | weight_internal: float = 1.0, 215 | weight_external_import: float = 0.5, 216 | weight_external_dependency: float = 0.5, 217 | analyze_markdown: bool = False 218 | ) -> Dict[str, any]: 219 | """ 220 | Main analysis function that calculates CodeRank scores. 221 | 222 | Returns a dictionary with: 223 | - module_ranks: Dict[module_fqn, score] 224 | - markdown_ranks: Dict[md_file_path, score] (if analyze_markdown=True) 225 | - module_map: Dict[module_fqn, file_path] 226 | - python_symbols_db: Dict[symbol_fqn, symbol_info] 227 | - import_graph: networkx.DiGraph 228 | """ 229 | if external_modules is None: 230 | external_modules = ["numpy", "pandas", "sklearn", "torch", "tensorflow", "requests", "django", "flask"] 231 | 232 | abs_repo_path = os.path.abspath(repo_path) 233 | if not os.path.isdir(abs_repo_path): 234 | raise ValueError(f"Repository path {abs_repo_path} not found or not a directory.") 235 | 236 | specified_external_modules = set(external_modules) 237 | 238 | # Discover Python files 239 | all_py_files = discover_python_files(abs_repo_path) 240 | if not all_py_files: 241 | return { 242 | "module_ranks": {}, 243 | "markdown_ranks": {}, 244 | "module_map": {}, 245 | "python_symbols_db": {}, 246 | "import_graph": nx.DiGraph() 247 | } 248 | 249 | module_map = {} # FQN -> file_path 250 | file_to_fqn = {} # file_path -> FQN 251 | internal_module_fqns = set() 252 | python_symbols_db = {} # Stores FQNs for modules, classes, functions, methods 253 | 254 | # Map files to module names and extract symbols 255 | for f_path in all_py_files: 256 | fqn = path_to_module_fqn(f_path, abs_repo_path) 257 | if fqn: 258 | module_map[fqn] = f_path 259 | file_to_fqn[f_path] = fqn 260 | internal_module_fqns.add(fqn) 261 | # Extract symbols for this file 262 | symbols = extract_python_symbols(f_path, fqn) 263 | python_symbols_db.update(symbols) 264 | 265 | # Initialize graphs 266 | G_imports = nx.DiGraph() # A imports B: A -> B 267 | G_imported_by = nx.DiGraph() # A imports B: B -> A (for PageRank "outgoing") 268 | 269 | all_graph_nodes = set(internal_module_fqns) 270 | for ext_mod in specified_external_modules: 271 | all_graph_nodes.add(ext_mod) 272 | 273 | # Add all potential nodes 274 | for node_fqn in all_graph_nodes: 275 | G_imports.add_node(node_fqn) 276 | G_imported_by.add_node(node_fqn) 277 | 278 | # Parse imports and build dependency graphs 279 | for f_path in all_py_files: 280 | current_fqn = file_to_fqn.get(f_path) 281 | if not current_fqn: 282 | continue 283 | 284 | imported_fqns = get_imports_from_file(f_path, current_fqn) 285 | 286 | for imported_fqn_full in imported_fqns: 287 | target_node_fqn = None 288 | is_external = False 289 | weight_to = weight_internal 290 | weight_from = weight_internal 291 | 292 | # Check if it's a specified external module 293 | imported_root = imported_fqn_full.split('.')[0] 294 | if imported_root in specified_external_modules: 295 | target_node_fqn = imported_root 296 | is_external = True 297 | weight_to = weight_external_import 298 | weight_from = weight_external_dependency 299 | # Check if it's an internal module 300 | elif imported_fqn_full in internal_module_fqns: 301 | target_node_fqn = imported_fqn_full 302 | 303 | if target_node_fqn and current_fqn != target_node_fqn: 304 | G_imports.add_edge(current_fqn, target_node_fqn, weight=weight_to) 305 | G_imported_by.add_edge(target_node_fqn, current_fqn, weight=weight_from) 306 | 307 | # Calculate PageRank 308 | try: 309 | pagerank_being_imported = nx.pagerank(G_imports, alpha=damping_factor, weight='weight', tol=1.0e-8, max_iter=200) 310 | except nx.PowerIterationFailedConvergence: 311 | pagerank_being_imported = {node: 1.0 / len(G_imports) for node in G_imports.nodes()} 312 | 313 | try: 314 | pagerank_importing_others = nx.pagerank(G_imported_by, alpha=damping_factor, weight='weight', tol=1.0e-8, max_iter=200) 315 | except nx.PowerIterationFailedConvergence: 316 | pagerank_importing_others = {node: 1.0 / len(G_imported_by) for node in G_imported_by.nodes()} 317 | 318 | # Calculate final CodeRank scores 319 | code_ranks = {} 320 | for module_fqn in internal_module_fqns: 321 | score_being_imported = pagerank_being_imported.get(module_fqn, 0) 322 | score_importing_others = pagerank_importing_others.get(module_fqn, 0) 323 | code_ranks[module_fqn] = score_being_imported + score_importing_others 324 | 325 | # Markdown analysis 326 | markdown_ranks = {} 327 | if analyze_markdown: 328 | md_files = discover_markdown_files(abs_repo_path) 329 | if md_files: 330 | all_py_fqns_for_md_search = set(python_symbols_db.keys()) 331 | markdown_to_referenced_modules = defaultdict(set) 332 | 333 | for md_file in md_files: 334 | referenced_modules = analyze_markdown_file_references(md_file, all_py_fqns_for_md_search, python_symbols_db) 335 | if referenced_modules: 336 | markdown_to_referenced_modules[md_file].update(referenced_modules) 337 | 338 | # Rank Markdown files 339 | for md_file, referenced_mods in markdown_to_referenced_modules.items(): 340 | score = sum(code_ranks.get(mod_fqn, 0) for mod_fqn in referenced_mods) 341 | markdown_ranks[md_file] = score 342 | 343 | return { 344 | "module_ranks": code_ranks, 345 | "markdown_ranks": markdown_ranks, 346 | "module_map": module_map, 347 | "python_symbols_db": python_symbols_db, 348 | "import_graph": G_imports, 349 | "internal_modules": internal_module_fqns, 350 | "external_modules": specified_external_modules 351 | } -------------------------------------------------------------------------------- /github_tools.py: -------------------------------------------------------------------------------- 1 | 2 | from datetime import datetime, timedelta 3 | from typing import Dict, Set, List, Tuple 4 | import hashlib 5 | 6 | 7 | """ 8 | 1. **`analyze_recent_changes`** - Main tool that: 9 | - Analyzes commits from the last N days 10 | - Ranks changes by combining CodeRank scores with commit frequency, contributor count, and change size 11 | - Identifies the most important recent modifications 12 | 13 | 2. **`get_commit_hotspots`** - Finds modules frequently changed together: 14 | - Identifies hidden dependencies through co-change patterns 15 | - Helps spot potential architectural issues 16 | - Suggests refactoring opportunities 17 | 18 | 3. **`contributor_impact_analysis`** - Analyzes developer patterns: 19 | - Tracks which developers work on critical modules 20 | - Identifies domain experts based on commit patterns 21 | - Measures contributor impact weighted by module importance 22 | 23 | 4. **`change_propagation_analysis`** - Predicts change ripple effects: 24 | - Uses historical data to predict which modules will need changes 25 | - Identifies test files that typically change with a module 26 | - Provides risk assessment for proposed changes 27 | 28 | """ 29 | 30 | 31 | # =============================================== 32 | # Recent Changes Analysis Tools 33 | # =============================================== 34 | 35 | @mcp.tool() 36 | def analyze_recent_changes( 37 | repo_path: str, 38 | days_back: int = 30, 39 | target_branch: str = "main", 40 | min_commits: int = 2, 41 | top_n: int = 20, 42 | include_stats: bool = True 43 | ) -> str: 44 | """ 45 | Analyze recent changes using CodeRank to identify most important modifications. 46 | Aggregates changes over the last N days and ranks them by impact. 47 | 48 | Args: 49 | repo_path: Repository path (absolute) 50 | days_back: Number of days to look back for commits 51 | target_branch: Branch to analyze (default: main) 52 | min_commits: Minimum commits to a file to be considered 53 | top_n: Number of top changes to return 54 | include_stats: Include detailed statistics 55 | 56 | Returns: 57 | Ranked list of most important recent changes with metrics 58 | """ 59 | try: 60 | # First, get CodeRank scores for context 61 | coderank_results = calculate_coderank(repo_path=repo_path) 62 | module_scores = coderank_results["module_ranks"] 63 | module_map = coderank_results["module_map"] 64 | 65 | # Get recent commits 66 | since_date = (datetime.now() - timedelta(days=days_back)).strftime('%Y-%m-%d') 67 | 68 | # Get commit data with file changes 69 | git_log_cmd = [ 70 | "git", "-C", repo_path, "log", 71 | f"--since={since_date}", 72 | f"{target_branch}", 73 | "--name-status", 74 | "--format=%H|%ae|%an|%ad|%s", 75 | "--date=short" 76 | ] 77 | 78 | log_result = subprocess.run(git_log_cmd, capture_output=True, text=True) 79 | if log_result.returncode != 0: 80 | return f"Error getting git log: {log_result.stderr}" 81 | 82 | # Parse commits and changes 83 | commits_data = [] 84 | current_commit = None 85 | 86 | for line in log_result.stdout.split('\n'): 87 | if not line.strip(): 88 | continue 89 | 90 | if '|' in line and not line.startswith(('A\t', 'M\t', 'D\t')): 91 | # This is a commit line 92 | parts = line.split('|', 4) 93 | if len(parts) >= 5: 94 | if current_commit: 95 | commits_data.append(current_commit) 96 | 97 | current_commit = { 98 | 'hash': parts[0], 99 | 'author_email': parts[1], 100 | 'author_name': parts[2], 101 | 'date': parts[3], 102 | 'message': parts[4], 103 | 'files': [] 104 | } 105 | elif line.startswith(('A\t', 'M\t', 'D\t')) and current_commit: 106 | # This is a file change 107 | status, filepath = line.split('\t', 1) 108 | if filepath.endswith('.py'): 109 | current_commit['files'].append({ 110 | 'status': status, 111 | 'path': filepath 112 | }) 113 | 114 | if current_commit: 115 | commits_data.append(current_commit) 116 | 117 | # Aggregate changes by module 118 | module_changes = defaultdict(lambda: { 119 | 'commits': [], 120 | 'authors': set(), 121 | 'commit_count': 0, 122 | 'lines_changed': 0, 123 | 'coderank_score': 0, 124 | 'files': set() 125 | }) 126 | 127 | # Process each commit 128 | for commit in commits_data: 129 | for file_change in commit['files']: 130 | filepath = file_change['path'] 131 | abs_filepath = os.path.join(repo_path, filepath) 132 | 133 | # Convert to module name 134 | module_fqn = path_to_module_fqn(abs_filepath, repo_path) 135 | if not module_fqn: 136 | continue 137 | 138 | # Update module change data 139 | change_data = module_changes[module_fqn] 140 | change_data['commits'].append(commit['hash'][:7]) 141 | change_data['authors'].add(commit['author_name']) 142 | change_data['commit_count'] += 1 143 | change_data['files'].add(filepath) 144 | change_data['coderank_score'] = module_scores.get(module_fqn, 0) 145 | 146 | # Get line change statistics if requested 147 | if include_stats: 148 | for module_fqn, data in module_changes.items(): 149 | if data['commit_count'] >= min_commits: 150 | # Get diff stats for this module's files 151 | for filepath in data['files']: 152 | diff_cmd = [ 153 | "git", "-C", repo_path, "diff", 154 | f"--since={since_date}", 155 | f"{target_branch}", 156 | "--numstat", 157 | "--", filepath 158 | ] 159 | diff_result = subprocess.run(diff_cmd, capture_output=True, text=True) 160 | 161 | if diff_result.returncode == 0 and diff_result.stdout: 162 | lines = diff_result.stdout.strip().split('\n') 163 | for line in lines: 164 | if line: 165 | parts = line.split('\t') 166 | if len(parts) >= 2: 167 | try: 168 | added = int(parts[0]) if parts[0] != '-' else 0 169 | deleted = int(parts[1]) if parts[1] != '-' else 0 170 | data['lines_changed'] += added + deleted 171 | except ValueError: 172 | pass 173 | 174 | # Calculate change impact scores 175 | change_scores = [] 176 | 177 | for module_fqn, data in module_changes.items(): 178 | if data['commit_count'] < min_commits: 179 | continue 180 | 181 | # Calculate composite score 182 | impact_score = ( 183 | data['coderank_score'] * 1000 + # Module importance (heavily weighted) 184 | data['commit_count'] * 10 + # Frequency of changes 185 | len(data['authors']) * 50 + # Contributor diversity 186 | data['lines_changed'] * 0.1 # Size of changes 187 | ) 188 | 189 | change_scores.append({ 190 | 'module': module_fqn, 191 | 'impact_score': impact_score, 192 | 'coderank_score': data['coderank_score'], 193 | 'commit_count': data['commit_count'], 194 | 'unique_authors': len(data['authors']), 195 | 'lines_changed': data['lines_changed'], 196 | 'authors': list(data['authors']), 197 | 'recent_commits': data['commits'][-5:] # Last 5 commit hashes 198 | }) 199 | 200 | # Sort by impact score 201 | change_scores.sort(key=lambda x: x['impact_score'], reverse=True) 202 | top_changes = change_scores[:top_n] 203 | 204 | # Format output 205 | output_lines = ["=== Recent Changes Analysis (CodeRank-based) ===\n"] 206 | output_lines.append(f"Repository: {repo_path}") 207 | output_lines.append(f"Analysis period: Last {days_back} days") 208 | output_lines.append(f"Target branch: {target_branch}") 209 | output_lines.append(f"Total commits analyzed: {len(commits_data)}") 210 | output_lines.append(f"Modules with significant changes: {len(change_scores)}\n") 211 | 212 | output_lines.append("Top Changed Modules by Impact:") 213 | output_lines.append(f"{'Module'.ljust(40)} | {'Impact'.rjust(7)} | {'CodeRank'.rjust(8)} | {'Commits'.rjust(7)} | {'Authors'.rjust(7)} | {'Lines ±'.rjust(8)}") 214 | output_lines.append("-" * 95) 215 | 216 | for change in top_changes: 217 | output_lines.append( 218 | f"{change['module'][:40].ljust(40)} | " 219 | f"{change['impact_score']:7.1f} | " 220 | f"{change['coderank_score']:8.4f} | " 221 | f"{change['commit_count']:7d} | " 222 | f"{change['unique_authors']:7d} | " 223 | f"{change['lines_changed']:8d}" 224 | ) 225 | 226 | # Add insights 227 | output_lines.append("\n=== Key Insights ===") 228 | 229 | # Find high-impact changes 230 | high_impact = [c for c in top_changes if c['coderank_score'] > 0.01] 231 | if high_impact: 232 | output_lines.append(f"\n• Critical Module Changes ({len(high_impact)} modules):") 233 | for change in high_impact[:5]: 234 | output_lines.append(f" - {change['module']}: {change['commit_count']} commits by {change['unique_authors']} authors") 235 | 236 | # Find hotspots (many commits) 237 | hotspots = [c for c in top_changes if c['commit_count'] > 10] 238 | if hotspots: 239 | output_lines.append(f"\n• Change Hotspots (frequently modified):") 240 | for change in hotspots[:3]: 241 | output_lines.append(f" - {change['module']}: {change['commit_count']} commits") 242 | 243 | # Find collaborative changes 244 | collaborative = [c for c in top_changes if c['unique_authors'] > 3] 245 | if collaborative: 246 | output_lines.append(f"\n• Collaborative Development Areas:") 247 | for change in collaborative[:3]: 248 | authors = ", ".join(change['authors'][:3]) 249 | if len(change['authors']) > 3: 250 | authors += f" +{len(change['authors'])-3} more" 251 | output_lines.append(f" - {change['module']}: {authors}") 252 | 253 | return '\n'.join(output_lines) 254 | 255 | except Exception as e: 256 | return f"Error in analyze_recent_changes: {str(e)}" 257 | 258 | 259 | @mcp.tool() 260 | def get_commit_hotspots( 261 | repo_path: str, 262 | days_back: int = 30, 263 | min_cochange_frequency: int = 3, 264 | top_n: int = 10 265 | ) -> str: 266 | """ 267 | Find modules that are frequently changed together in commits. 268 | Identifies coupled modules that might have hidden dependencies. 269 | 270 | Args: 271 | repo_path: Repository path 272 | days_back: Days to analyze 273 | min_cochange_frequency: Minimum times modules must change together 274 | top_n: Number of top coupled module pairs to return 275 | 276 | Returns: 277 | Analysis of modules that frequently change together 278 | """ 279 | try: 280 | # Get CodeRank data for context 281 | coderank_results = calculate_coderank(repo_path=repo_path) 282 | module_scores = coderank_results["module_ranks"] 283 | 284 | since_date = (datetime.now() - timedelta(days=days_back)).strftime('%Y-%m-%d') 285 | 286 | # Get commits with changed files 287 | git_cmd = [ 288 | "git", "-C", repo_path, "log", 289 | f"--since={since_date}", 290 | "--name-only", 291 | "--format=%H|%an|%ae|%ad", 292 | "--date=short" 293 | ] 294 | 295 | result = subprocess.run(git_cmd, capture_output=True, text=True) 296 | if result.returncode != 0: 297 | return f"Error getting git log: {result.stderr}" 298 | 299 | # Parse commits and their files 300 | commits = [] 301 | current_commit = None 302 | 303 | for line in result.stdout.split('\n'): 304 | if '|' in line: 305 | if current_commit and current_commit['modules']: 306 | commits.append(current_commit) 307 | 308 | parts = line.split('|') 309 | current_commit = { 310 | 'hash': parts[0], 311 | 'author': parts[1], 312 | 'email': parts[2], 313 | 'date': parts[3], 314 | 'modules': set() 315 | } 316 | elif line.strip() and line.endswith('.py') and current_commit: 317 | # Convert file to module 318 | abs_path = os.path.join(repo_path, line.strip()) 319 | module_fqn = path_to_module_fqn(abs_path, repo_path) 320 | if module_fqn: 321 | current_commit['modules'].add(module_fqn) 322 | 323 | if current_commit and current_commit['modules']: 324 | commits.append(current_commit) 325 | 326 | # Find co-occurring modules 327 | cochange_pairs = defaultdict(lambda: { 328 | 'count': 0, 329 | 'commits': [], 330 | 'authors': set(), 331 | 'combined_coderank': 0 332 | }) 333 | 334 | for commit in commits: 335 | modules = list(commit['modules']) 336 | if len(modules) < 2: 337 | continue 338 | 339 | # Generate all pairs 340 | for i in range(len(modules)): 341 | for j in range(i + 1, len(modules)): 342 | pair = tuple(sorted([modules[i], modules[j]])) 343 | data = cochange_pairs[pair] 344 | data['count'] += 1 345 | data['commits'].append(commit['hash'][:7]) 346 | data['authors'].add(commit['author']) 347 | 348 | # Calculate combined CodeRank score 349 | score1 = module_scores.get(modules[i], 0) 350 | score2 = module_scores.get(modules[j], 0) 351 | data['combined_coderank'] = score1 + score2 352 | 353 | # Filter and sort 354 | significant_pairs = [] 355 | for pair, data in cochange_pairs.items(): 356 | if data['count'] >= min_cochange_frequency: 357 | significant_pairs.append({ 358 | 'modules': pair, 359 | 'frequency': data['count'], 360 | 'unique_authors': len(data['authors']), 361 | 'combined_coderank': data['combined_coderank'], 362 | 'coupling_score': data['count'] * data['combined_coderank'] * 100, 363 | 'recent_commits': data['commits'][-3:] 364 | }) 365 | 366 | significant_pairs.sort(key=lambda x: x['coupling_score'], reverse=True) 367 | top_pairs = significant_pairs[:top_n] 368 | 369 | # Format output 370 | output_lines = ["=== Commit Hotspot Analysis ===\n"] 371 | output_lines.append(f"Repository: {repo_path}") 372 | output_lines.append(f"Period: Last {days_back} days") 373 | output_lines.append(f"Total commits analyzed: {len(commits)}") 374 | output_lines.append(f"Coupled module pairs found: {len(significant_pairs)}\n") 375 | 376 | output_lines.append("Top Coupled Modules (frequently changed together):") 377 | output_lines.append(f"{'Module 1'.ljust(30)} | {'Module 2'.ljust(30)} | {'Freq'.rjust(4)} | {'Score'.rjust(7)}") 378 | output_lines.append("-" * 80) 379 | 380 | for pair_data in top_pairs: 381 | mod1, mod2 = pair_data['modules'] 382 | output_lines.append( 383 | f"{mod1[:30].ljust(30)} | " 384 | f"{mod2[:30].ljust(30)} | " 385 | f"{pair_data['frequency']:4d} | " 386 | f"{pair_data['coupling_score']:7.1f}" 387 | ) 388 | 389 | # Add insights 390 | output_lines.append("\n=== Coupling Insights ===") 391 | 392 | # Find high-importance coupled modules 393 | high_importance_pairs = [p for p in top_pairs if p['combined_coderank'] > 0.02] 394 | if high_importance_pairs: 395 | output_lines.append("\n• High-importance coupled modules:") 396 | for pair in high_importance_pairs[:3]: 397 | output_lines.append(f" - {pair['modules'][0]} ↔ {pair['modules'][1]}") 398 | output_lines.append(f" Changed together {pair['frequency']} times") 399 | 400 | # Find potential refactoring candidates 401 | high_coupling = [p for p in top_pairs if p['frequency'] > 10] 402 | if high_coupling: 403 | output_lines.append("\n• Potential refactoring candidates (high coupling):") 404 | for pair in high_coupling[:3]: 405 | output_lines.append(f" - {pair['modules'][0]} & {pair['modules'][1]}") 406 | output_lines.append(f" Consider: merge, extract common interface, or clarify boundaries") 407 | 408 | return '\n'.join(output_lines) 409 | 410 | except Exception as e: 411 | return f"Error in get_commit_hotspots: {str(e)}" 412 | 413 | 414 | @mcp.tool() 415 | def contributor_impact_analysis( 416 | repo_path: str, 417 | days_back: int = 90, 418 | min_commits: int = 5, 419 | focus_on_important_modules: bool = True 420 | ) -> str: 421 | """ 422 | Analyze contributor patterns and their impact on important modules. 423 | Identifies key contributors and their areas of expertise. 424 | 425 | Args: 426 | repo_path: Repository path 427 | days_back: Days to analyze 428 | min_commits: Minimum commits by a contributor to be included 429 | focus_on_important_modules: Weight contributions by module importance 430 | 431 | Returns: 432 | Contributor impact analysis with expertise areas 433 | """ 434 | try: 435 | # Get CodeRank data 436 | coderank_results = calculate_coderank(repo_path=repo_path) 437 | module_scores = coderank_results["module_ranks"] 438 | 439 | since_date = (datetime.now() - timedelta(days=days_back)).strftime('%Y-%m-%d') 440 | 441 | # Get detailed commit data 442 | git_cmd = [ 443 | "git", "-C", repo_path, "log", 444 | f"--since={since_date}", 445 | "--name-status", 446 | "--format=%H|%ae|%an|%ad|%s", 447 | "--date=short" 448 | ] 449 | 450 | result = subprocess.run(git_cmd, capture_output=True, text=True) 451 | if result.returncode != 0: 452 | return f"Error getting git log: {result.stderr}" 453 | 454 | # Parse contributor data 455 | contributor_data = defaultdict(lambda: { 456 | 'commits': 0, 457 | 'modules_touched': set(), 458 | 'impact_score': 0, 459 | 'lines_added': 0, 460 | 'lines_removed': 0, 461 | 'important_module_commits': 0, 462 | 'recent_commits': [] 463 | }) 464 | 465 | current_commit = None 466 | for line in result.stdout.split('\n'): 467 | if '|' in line and not line.startswith(('A\t', 'M\t', 'D\t')): 468 | parts = line.split('|', 4) 469 | if len(parts) >= 5: 470 | current_commit = { 471 | 'hash': parts[0], 472 | 'email': parts[1], 473 | 'name': parts[2], 474 | 'date': parts[3], 475 | 'message': parts[4], 476 | 'modules': set() 477 | } 478 | elif line.startswith(('A\t', 'M\t', 'D\t')) and current_commit: 479 | status, filepath = line.split('\t', 1) 480 | if filepath.endswith('.py'): 481 | abs_path = os.path.join(repo_path, filepath) 482 | module_fqn = path_to_module_fqn(abs_path, repo_path) 483 | 484 | if module_fqn: 485 | contributor = contributor_data[current_commit['name']] 486 | contributor['commits'] += 1 487 | contributor['modules_touched'].add(module_fqn) 488 | contributor['recent_commits'].append(current_commit['hash'][:7]) 489 | 490 | # Track important module contributions 491 | module_score = module_scores.get(module_fqn, 0) 492 | if module_score > 0.01: 493 | contributor['important_module_commits'] += 1 494 | 495 | if focus_on_important_modules: 496 | contributor['impact_score'] += module_score * 100 497 | 498 | # Get line statistics for top contributors 499 | active_contributors = {name: data for name, data in contributor_data.items() 500 | if data['commits'] >= min_commits} 501 | 502 | for name, data in active_contributors.items(): 503 | # Get contributor's line changes 504 | stat_cmd = [ 505 | "git", "-C", repo_path, "log", 506 | f"--author={name}", 507 | f"--since={since_date}", 508 | "--pretty=tformat:", 509 | "--numstat" 510 | ] 511 | 512 | stat_result = subprocess.run(stat_cmd, capture_output=True, text=True) 513 | if stat_result.returncode == 0: 514 | for line in stat_result.stdout.split('\n'): 515 | if line.strip(): 516 | parts = line.split('\t') 517 | if len(parts) >= 3 and parts[2].endswith('.py'): 518 | try: 519 | added = int(parts[0]) if parts[0] != '-' else 0 520 | removed = int(parts[1]) if parts[1] != '-' else 0 521 | data['lines_added'] += added 522 | data['lines_removed'] += removed 523 | except ValueError: 524 | pass 525 | 526 | # Calculate final impact scores 527 | contributor_scores = [] 528 | for name, data in active_contributors.items(): 529 | if not focus_on_important_modules: 530 | # Alternative scoring without module importance 531 | data['impact_score'] = ( 532 | data['commits'] * 10 + 533 | len(data['modules_touched']) * 5 + 534 | (data['lines_added'] + data['lines_removed']) * 0.01 535 | ) 536 | 537 | # Find expertise areas (most touched modules) 538 | module_touches = defaultdict(int) 539 | for module in data['modules_touched']: 540 | module_touches[module] += 1 541 | 542 | expertise_areas = sorted( 543 | [(m, c) for m, c in module_touches.items()], 544 | key=lambda x: (x[1], module_scores.get(x[0], 0)), 545 | reverse=True 546 | )[:3] 547 | 548 | contributor_scores.append({ 549 | 'name': name, 550 | 'impact_score': data['impact_score'], 551 | 'commits': data['commits'], 552 | 'modules_touched': len(data['modules_touched']), 553 | 'lines_changed': data['lines_added'] + data['lines_removed'], 554 | 'important_module_commits': data['important_module_commits'], 555 | 'expertise_areas': expertise_areas 556 | }) 557 | 558 | # Sort by impact 559 | contributor_scores.sort(key=lambda x: x['impact_score'], reverse=True) 560 | 561 | # Format output 562 | output_lines = ["=== Contributor Impact Analysis ===\n"] 563 | output_lines.append(f"Repository: {repo_path}") 564 | output_lines.append(f"Period: Last {days_back} days") 565 | output_lines.append(f"Active contributors: {len(contributor_scores)}") 566 | output_lines.append(f"Module importance weighting: {'Enabled' if focus_on_important_modules else 'Disabled'}\n") 567 | 568 | output_lines.append("Top Contributors by Impact:") 569 | output_lines.append(f"{'Contributor'.ljust(25)} | {'Impact'.rjust(7)} | {'Commits'.rjust(7)} | {'Modules'.rjust(7)} | {'Lines ±'.rjust(8)} | {'Critical'.rjust(8)}") 570 | output_lines.append("-" * 85) 571 | 572 | for contributor in contributor_scores[:15]: 573 | output_lines.append( 574 | f"{contributor['name'][:25].ljust(25)} | " 575 | f"{contributor['impact_score']:7.1f} | " 576 | f"{contributor['commits']:7d} | " 577 | f"{contributor['modules_touched']:7d} | " 578 | f"{contributor['lines_changed']:8d} | " 579 | f"{contributor['important_module_commits']:8d}" 580 | ) 581 | 582 | # Add expertise breakdown 583 | output_lines.append("\n=== Contributor Expertise Areas ===") 584 | 585 | for contributor in contributor_scores[:10]: 586 | if contributor['expertise_areas']: 587 | output_lines.append(f"\n{contributor['name']}:") 588 | for module, touch_count in contributor['expertise_areas']: 589 | module_score = module_scores.get(module, 0) 590 | importance = "HIGH" if module_score > 0.01 else "normal" 591 | output_lines.append(f" • {module} ({touch_count} commits, {importance} importance)") 592 | 593 | # Add insights 594 | output_lines.append("\n=== Key Insights ===") 595 | 596 | # Find domain experts 597 | critical_contributors = [c for c in contributor_scores 598 | if c['important_module_commits'] > 10] 599 | if critical_contributors: 600 | output_lines.append("\n• Critical module experts:") 601 | for contrib in critical_contributors[:3]: 602 | output_lines.append(f" - {contrib['name']}: {contrib['important_module_commits']} commits to critical modules") 603 | 604 | # Find broad contributors 605 | broad_contributors = [c for c in contributor_scores 606 | if c['modules_touched'] > 20] 607 | if broad_contributors: 608 | output_lines.append("\n• Broad impact contributors:") 609 | for contrib in broad_contributors[:3]: 610 | output_lines.append(f" - {contrib['name']}: touched {contrib['modules_touched']} different modules") 611 | 612 | return '\n'.join(output_lines) 613 | 614 | except Exception as e: 615 | return f"Error in contributor_impact_analysis: {str(e)}" 616 | 617 | 618 | @mcp.tool() 619 | def change_propagation_analysis( 620 | repo_path: str, 621 | changed_module: str, 622 | days_back: int = 90, 623 | include_test_impact: bool = True 624 | ) -> str: 625 | """ 626 | Analyze how changes in one module historically propagate to others. 627 | Uses commit history to predict ripple effects of changes. 628 | 629 | Args: 630 | repo_path: Repository path 631 | changed_module: Module to analyze (e.g., 'src.auth.user') 632 | days_back: Days of history to analyze 633 | include_test_impact: Include analysis of test file changes 634 | 635 | Returns: 636 | Analysis of likely modules to be affected by changes 637 | """ 638 | try: 639 | # Get CodeRank data 640 | coderank_results = calculate_coderank(repo_path=repo_path) 641 | module_scores = coderank_results["module_ranks"] 642 | import_graph = coderank_results["import_graph"] 643 | 644 | if changed_module not in module_scores: 645 | return f"Module '{changed_module}' not found. Available modules: {', '.join(list(module_scores.keys())[:10])}..." 646 | 647 | since_date = (datetime.now() - timedelta(days=days_back)).strftime('%Y-%m-%d') 648 | 649 | # Find commits that touched the target module 650 | module_file = coderank_results["module_map"].get(changed_module) 651 | if not module_file: 652 | return f"Could not find file for module {changed_module}" 653 | 654 | rel_module_file = os.path.relpath(module_file, repo_path) 655 | 656 | # Get commits that changed this module 657 | git_cmd = [ 658 | "git", "-C", repo_path, "log", 659 | f"--since={since_date}", 660 | "--format=%H", 661 | "--", rel_module_file 662 | ] 663 | 664 | result = subprocess.run(git_cmd, capture_output=True, text=True) 665 | if result.returncode != 0: 666 | return f"Error getting git log: {result.stderr}" 667 | 668 | target_commits = result.stdout.strip().split('\n') 669 | if not target_commits or not target_commits[0]: 670 | return f"No commits found for module {changed_module} in the last {days_back} days" 671 | 672 | # For each commit, find what else changed 673 | propagation_data = defaultdict(lambda: { 674 | 'co_change_count': 0, 675 | 'commits': [], 676 | 'is_import_related': False, 677 | 'is_test': False, 678 | 'module_score': 0 679 | }) 680 | 681 | for commit_hash in target_commits: 682 | # Get all files changed in this commit 683 | files_cmd = [ 684 | "git", "-C", repo_path, "show", 685 | "--name-only", 686 | "--format=", 687 | commit_hash 688 | ] 689 | 690 | files_result = subprocess.run(files_cmd, capture_output=True, text=True) 691 | if files_result.returncode == 0: 692 | changed_files = files_result.stdout.strip().split('\n') 693 | 694 | for filepath in changed_files: 695 | if filepath and filepath.endswith('.py') and filepath != rel_module_file: 696 | abs_filepath = os.path.join(repo_path, filepath) 697 | other_module = path_to_module_fqn(abs_filepath, repo_path) 698 | 699 | if other_module: 700 | data = propagation_data[other_module] 701 | data['co_change_count'] += 1 702 | data['commits'].append(commit_hash[:7]) 703 | data['module_score'] = module_scores.get(other_module, 0) 704 | 705 | # Check if it's a test file 706 | if 'test' in filepath.lower(): 707 | data['is_test'] = True 708 | 709 | # Check if there's an import relationship 710 | if (other_module in import_graph.successors(changed_module) or 711 | changed_module in import_graph.successors(other_module)): 712 | data['is_import_related'] = True 713 | 714 | # Calculate propagation scores 715 | propagation_scores = [] 716 | for module, data in propagation_data.items(): 717 | # Skip tests if not requested 718 | if not include_test_impact and data['is_test']: 719 | continue 720 | 721 | # Calculate likelihood score 722 | propagation_score = ( 723 | data['co_change_count'] * 10 + # Frequency 724 | (50 if data['is_import_related'] else 0) + # Import relationship 725 | data['module_score'] * 100 # Module importance 726 | ) 727 | 728 | propagation_scores.append({ 729 | 'module': module, 730 | 'score': propagation_score, 731 | 'co_changes': data['co_change_count'], 732 | 'probability': data['co_change_count'] / len(target_commits), 733 | 'is_import_related': data['is_import_related'], 734 | 'is_test': data['is_test'], 735 | 'module_importance': data['module_score'] 736 | }) 737 | 738 | # Sort by score 739 | propagation_scores.sort(key=lambda x: x['score'], reverse=True) 740 | 741 | # Format output 742 | output_lines = ["=== Change Propagation Analysis ===\n"] 743 | output_lines.append(f"Target Module: {changed_module}") 744 | output_lines.append(f"Module Importance: {module_scores[changed_module]:.4f}") 745 | output_lines.append(f"Analysis Period: Last {days_back} days") 746 | output_lines.append(f"Commits analyzed: {len(target_commits)}") 747 | output_lines.append(f"Include test impact: {include_test_impact}\n") 748 | 749 | # Direct dependencies from import graph 750 | direct_importers = list(import_graph.predecessors(changed_module)) 751 | direct_imports = list(import_graph.successors(changed_module)) 752 | 753 | output_lines.append("Direct Dependencies:") 754 | output_lines.append(f" • Imports from {changed_module}: {len(direct_importers)} modules") 755 | output_lines.append(f" • {changed_module} imports: {len(direct_imports)} modules\n") 756 | 757 | output_lines.append("Likely Affected Modules (based on historical patterns):") 758 | output_lines.append(f"{'Module'.ljust(35)} | {'Likelihood'.rjust(10)} | {'Co-changes'.rjust(10)} | {'Import Link'} | {'Type'}") 759 | output_lines.append("-" * 85) 760 | 761 | for prop in propagation_scores[:20]: 762 | import_marker = "Yes" if prop['is_import_related'] else "No" 763 | module_type = "Test" if prop['is_test'] else "Code" 764 | 765 | output_lines.append( 766 | f"{prop['module'][:35].ljust(35)} | " 767 | f"{prop['probability']:10.1%} | " 768 | f"{prop['co_changes']:10d} | " 769 | f"{import_marker:11} | " 770 | f"{module_type}" 771 | ) 772 | 773 | # Add insights 774 | output_lines.append("\n=== Propagation Insights ===") 775 | 776 | # High probability changes 777 | high_prob = [p for p in propagation_scores if p['probability'] > 0.5] 778 | if high_prob: 779 | output_lines.append(f"\n• High probability ripple effects ({len(high_prob)} modules):") 780 | for prop in high_prob[:5]: 781 | reason = "import dependency" if prop['is_import_related'] else "historical coupling" 782 | output_lines.append(f" - {prop['module']} ({prop['probability']:.0%} chance, {reason})") 783 | 784 | # Test impact 785 | if include_test_impact: 786 | test_impacts = [p for p in propagation_scores if p['is_test']] 787 | if test_impacts: 788 | output_lines.append(f"\n• Test files likely to need updates ({len(test_impacts)} files):") 789 | for prop in test_impacts[:5]: 790 | output_lines.append(f" - {prop['module']} ({prop['probability']:.0%} historical correlation)") 791 | 792 | # Import-related changes 793 | import_related = [p for p in propagation_scores if p['is_import_related']] 794 | if import_related: 795 | output_lines.append(f"\n• Import-dependent modules ({len(import_related)} modules):") 796 | for prop in import_related[:5]: 797 | output_lines.append(f" - {prop['module']} (direct import relationship)") 798 | 799 | # Risk assessment 800 | output_lines.append("\n=== Change Risk Assessment ===") 801 | total_risk_score = sum(p['score'] for p in propagation_scores[:10]) 802 | 803 | if total_risk_score > 1000: 804 | risk_level = "HIGH" 805 | output_lines.append("• Risk Level: HIGH - Many dependent modules likely affected") 806 | elif total_risk_score > 500: 807 | risk_level = "MEDIUM" 808 | output_lines.append("• Risk Level: MEDIUM - Moderate ripple effects expected") 809 | else: 810 | risk_level = "LOW" 811 | output_lines.append("• Risk Level: LOW - Limited propagation expected") 812 | 813 | output_lines.append(f"• Estimated modules affected: {len([p for p in propagation_scores if p['probability'] > 0.3])}") 814 | output_lines.append(f"• Recommended: Review and test the top {min(10, len(propagation_scores))} affected modules") 815 | 816 | return '\n'.join(output_lines) 817 | 818 | except Exception as e: 819 | return f"Error in change_propagation_analysis: {str(e)}" 820 | 821 | 822 | # Add a helper function that's not exposed as a tool 823 | def path_to_module_fqn(file_path: str, repo_path: str) -> Optional[str]: 824 | """Convert file path to module FQN - reuse from coderank.py""" 825 | from coderank import path_to_module_fqn as _path_to_module_fqn 826 | return _path_to_module_fqn(file_path, repo_path) 827 | 828 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 3 3 | requires-python = ">=3.13" 4 | 5 | [[package]] 6 | name = "annotated-types" 7 | version = "0.7.0" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, 12 | ] 13 | 14 | [[package]] 15 | name = "anthropic" 16 | version = "0.54.0" 17 | source = { registry = "https://pypi.org/simple" } 18 | dependencies = [ 19 | { name = "anyio" }, 20 | { name = "distro" }, 21 | { name = "httpx" }, 22 | { name = "jiter" }, 23 | { name = "pydantic" }, 24 | { name = "sniffio" }, 25 | { name = "typing-extensions" }, 26 | ] 27 | sdist = { url = "https://files.pythonhosted.org/packages/89/28/80cb9bb6e7ce77d404145b51da4257455805c17f0a6be528ff3286e3882f/anthropic-0.54.0.tar.gz", hash = "sha256:5e6f997d97ce8e70eac603c3ec2e7f23addeff953fbbb76b19430562bb6ba815", size = 312376, upload-time = "2025-06-11T02:46:27.642Z" } 28 | wheels = [ 29 | { url = "https://files.pythonhosted.org/packages/de/b9/6ffb48e82c5e97b03cecee872d134a6b6666c2767b2d32ed709f3a60a8fe/anthropic-0.54.0-py3-none-any.whl", hash = "sha256:c1062a0a905daeec17ca9c06c401e4b3f24cb0495841d29d752568a1d4018d56", size = 288774, upload-time = "2025-06-11T02:46:25.578Z" }, 30 | ] 31 | 32 | [[package]] 33 | name = "anyio" 34 | version = "4.9.0" 35 | source = { registry = "https://pypi.org/simple" } 36 | dependencies = [ 37 | { name = "idna" }, 38 | { name = "sniffio" }, 39 | ] 40 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } 41 | wheels = [ 42 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, 43 | ] 44 | 45 | [[package]] 46 | name = "cachetools" 47 | version = "5.5.2" 48 | source = { registry = "https://pypi.org/simple" } 49 | sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } 50 | wheels = [ 51 | { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, 52 | ] 53 | 54 | [[package]] 55 | name = "cased-kit" 56 | version = "1.2.3" 57 | source = { registry = "https://pypi.org/simple" } 58 | dependencies = [ 59 | { name = "anthropic" }, 60 | { name = "click" }, 61 | { name = "fastapi" }, 62 | { name = "google-genai" }, 63 | { name = "mcp" }, 64 | { name = "numpy" }, 65 | { name = "openai" }, 66 | { name = "pathspec" }, 67 | { name = "python-hcl2" }, 68 | { name = "pyyaml" }, 69 | { name = "redis" }, 70 | { name = "requests" }, 71 | { name = "tiktoken" }, 72 | { name = "tree-sitter-language-pack" }, 73 | { name = "typer" }, 74 | { name = "uvicorn", extra = ["standard"] }, 75 | ] 76 | sdist = { url = "https://files.pythonhosted.org/packages/df/93/bf5782f25863d7770ad6e1a3455fa2178fbd053343103ac5aaf1d1429621/cased_kit-1.2.3.tar.gz", hash = "sha256:0abb8adcfe35b18cf03b212ddf431c7c3bd484c940d4731d99d72d72e6ab98f3", size = 231053, upload-time = "2025-06-21T20:34:08.198Z" } 77 | wheels = [ 78 | { url = "https://files.pythonhosted.org/packages/ad/cc/40475efcdad5aa3125fd5840caa75da2d9f8d7379e19c801d191d3d6e008/cased_kit-1.2.3-py3-none-any.whl", hash = "sha256:94cbdeca9c416eda364cae8e66b0b27ef3b8efda4375b3a82d278865c50593b7", size = 159488, upload-time = "2025-06-21T20:34:06.618Z" }, 79 | ] 80 | 81 | [[package]] 82 | name = "certifi" 83 | version = "2025.6.15" 84 | source = { registry = "https://pypi.org/simple" } 85 | sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } 86 | wheels = [ 87 | { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, 88 | ] 89 | 90 | [[package]] 91 | name = "charset-normalizer" 92 | version = "3.4.2" 93 | source = { registry = "https://pypi.org/simple" } 94 | sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } 95 | wheels = [ 96 | { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, 97 | { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, 98 | { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, 99 | { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, 100 | { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, 101 | { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, 102 | { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, 103 | { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, 104 | { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, 105 | { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, 106 | { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, 107 | { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, 108 | { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, 109 | { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, 110 | ] 111 | 112 | [[package]] 113 | name = "click" 114 | version = "8.1.8" 115 | source = { registry = "https://pypi.org/simple" } 116 | dependencies = [ 117 | { name = "colorama", marker = "sys_platform == 'win32'" }, 118 | ] 119 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } 120 | wheels = [ 121 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, 122 | ] 123 | 124 | [[package]] 125 | name = "colorama" 126 | version = "0.4.6" 127 | source = { registry = "https://pypi.org/simple" } 128 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 129 | wheels = [ 130 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 131 | ] 132 | 133 | [[package]] 134 | name = "distro" 135 | version = "1.9.0" 136 | source = { registry = "https://pypi.org/simple" } 137 | sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } 138 | wheels = [ 139 | { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, 140 | ] 141 | 142 | [[package]] 143 | name = "fastapi" 144 | version = "0.115.13" 145 | source = { registry = "https://pypi.org/simple" } 146 | dependencies = [ 147 | { name = "pydantic" }, 148 | { name = "starlette" }, 149 | { name = "typing-extensions" }, 150 | ] 151 | sdist = { url = "https://files.pythonhosted.org/packages/20/64/ec0788201b5554e2a87c49af26b77a4d132f807a0fa9675257ac92c6aa0e/fastapi-0.115.13.tar.gz", hash = "sha256:55d1d25c2e1e0a0a50aceb1c8705cd932def273c102bff0b1c1da88b3c6eb307", size = 295680, upload-time = "2025-06-17T11:49:45.575Z" } 152 | wheels = [ 153 | { url = "https://files.pythonhosted.org/packages/59/4a/e17764385382062b0edbb35a26b7cf76d71e27e456546277a42ba6545c6e/fastapi-0.115.13-py3-none-any.whl", hash = "sha256:0a0cab59afa7bab22f5eb347f8c9864b681558c278395e94035a741fc10cd865", size = 95315, upload-time = "2025-06-17T11:49:44.106Z" }, 154 | ] 155 | 156 | [[package]] 157 | name = "google-auth" 158 | version = "2.40.3" 159 | source = { registry = "https://pypi.org/simple" } 160 | dependencies = [ 161 | { name = "cachetools" }, 162 | { name = "pyasn1-modules" }, 163 | { name = "rsa" }, 164 | ] 165 | sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } 166 | wheels = [ 167 | { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, 168 | ] 169 | 170 | [[package]] 171 | name = "google-genai" 172 | version = "1.21.1" 173 | source = { registry = "https://pypi.org/simple" } 174 | dependencies = [ 175 | { name = "anyio" }, 176 | { name = "google-auth" }, 177 | { name = "httpx" }, 178 | { name = "pydantic" }, 179 | { name = "requests" }, 180 | { name = "tenacity" }, 181 | { name = "typing-extensions" }, 182 | { name = "websockets" }, 183 | ] 184 | sdist = { url = "https://files.pythonhosted.org/packages/49/8a/4a628e2e918f8c4a48ea778b68395b97b05b6873c6e528e78ccfb02a2c8d/google_genai-1.21.1.tar.gz", hash = "sha256:5412fde7f0b39574a4670a9a25e398824a12b3cddd632fdff66d1b9bcfdbfcb4", size = 205636, upload-time = "2025-06-19T14:09:20.466Z" } 185 | wheels = [ 186 | { url = "https://files.pythonhosted.org/packages/92/5c/659c2b992d631a873ae8fed612ce92af423fdc5f7d541dec7ce8f4b1789e/google_genai-1.21.1-py3-none-any.whl", hash = "sha256:fa6fa5311f9a757ce65cd528a938a0f309bb3032516015bf5b3022e63b2fc46b", size = 206388, upload-time = "2025-06-19T14:09:19.016Z" }, 187 | ] 188 | 189 | [[package]] 190 | name = "griffe" 191 | version = "1.14.0" 192 | source = { registry = "https://pypi.org/simple" } 193 | dependencies = [ 194 | { name = "colorama" }, 195 | ] 196 | sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/6c09dd7ce4c7837e4cdb11dce980cb45ae3cd87677298dc3b781b6bce7d3/griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13", size = 424684, upload-time = "2025-09-05T15:02:29.167Z" } 197 | wheels = [ 198 | { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439, upload-time = "2025-09-05T15:02:27.511Z" }, 199 | ] 200 | 201 | [[package]] 202 | name = "h11" 203 | version = "0.16.0" 204 | source = { registry = "https://pypi.org/simple" } 205 | sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } 206 | wheels = [ 207 | { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, 208 | ] 209 | 210 | [[package]] 211 | name = "httpcore" 212 | version = "1.0.9" 213 | source = { registry = "https://pypi.org/simple" } 214 | dependencies = [ 215 | { name = "certifi" }, 216 | { name = "h11" }, 217 | ] 218 | sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } 219 | wheels = [ 220 | { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, 221 | ] 222 | 223 | [[package]] 224 | name = "httptools" 225 | version = "0.6.4" 226 | source = { registry = "https://pypi.org/simple" } 227 | sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } 228 | wheels = [ 229 | { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214, upload-time = "2024-10-16T19:44:38.738Z" }, 230 | { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431, upload-time = "2024-10-16T19:44:39.818Z" }, 231 | { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121, upload-time = "2024-10-16T19:44:41.189Z" }, 232 | { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805, upload-time = "2024-10-16T19:44:42.384Z" }, 233 | { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858, upload-time = "2024-10-16T19:44:43.959Z" }, 234 | { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042, upload-time = "2024-10-16T19:44:45.071Z" }, 235 | { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682, upload-time = "2024-10-16T19:44:46.46Z" }, 236 | ] 237 | 238 | [[package]] 239 | name = "httpx" 240 | version = "0.28.1" 241 | source = { registry = "https://pypi.org/simple" } 242 | dependencies = [ 243 | { name = "anyio" }, 244 | { name = "certifi" }, 245 | { name = "httpcore" }, 246 | { name = "idna" }, 247 | ] 248 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } 249 | wheels = [ 250 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, 251 | ] 252 | 253 | [[package]] 254 | name = "httpx-sse" 255 | version = "0.4.0" 256 | source = { registry = "https://pypi.org/simple" } 257 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624, upload-time = "2023-12-22T08:01:21.083Z" } 258 | wheels = [ 259 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" }, 260 | ] 261 | 262 | [[package]] 263 | name = "idna" 264 | version = "3.10" 265 | source = { registry = "https://pypi.org/simple" } 266 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } 267 | wheels = [ 268 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, 269 | ] 270 | 271 | [[package]] 272 | name = "jiter" 273 | version = "0.10.0" 274 | source = { registry = "https://pypi.org/simple" } 275 | sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759, upload-time = "2025-05-18T19:04:59.73Z" } 276 | wheels = [ 277 | { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617, upload-time = "2025-05-18T19:04:02.078Z" }, 278 | { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947, upload-time = "2025-05-18T19:04:03.347Z" }, 279 | { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618, upload-time = "2025-05-18T19:04:04.709Z" }, 280 | { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829, upload-time = "2025-05-18T19:04:06.912Z" }, 281 | { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034, upload-time = "2025-05-18T19:04:08.222Z" }, 282 | { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529, upload-time = "2025-05-18T19:04:09.566Z" }, 283 | { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671, upload-time = "2025-05-18T19:04:10.98Z" }, 284 | { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864, upload-time = "2025-05-18T19:04:12.722Z" }, 285 | { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989, upload-time = "2025-05-18T19:04:14.261Z" }, 286 | { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495, upload-time = "2025-05-18T19:04:15.603Z" }, 287 | { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289, upload-time = "2025-05-18T19:04:17.541Z" }, 288 | { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074, upload-time = "2025-05-18T19:04:19.21Z" }, 289 | { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225, upload-time = "2025-05-18T19:04:20.583Z" }, 290 | { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235, upload-time = "2025-05-18T19:04:22.363Z" }, 291 | { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278, upload-time = "2025-05-18T19:04:23.627Z" }, 292 | { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866, upload-time = "2025-05-18T19:04:24.891Z" }, 293 | { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772, upload-time = "2025-05-18T19:04:26.161Z" }, 294 | { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534, upload-time = "2025-05-18T19:04:27.495Z" }, 295 | { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087, upload-time = "2025-05-18T19:04:28.896Z" }, 296 | { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694, upload-time = "2025-05-18T19:04:30.183Z" }, 297 | { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992, upload-time = "2025-05-18T19:04:32.028Z" }, 298 | { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723, upload-time = "2025-05-18T19:04:33.467Z" }, 299 | { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215, upload-time = "2025-05-18T19:04:34.827Z" }, 300 | { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762, upload-time = "2025-05-18T19:04:36.19Z" }, 301 | { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427, upload-time = "2025-05-18T19:04:37.544Z" }, 302 | { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127, upload-time = "2025-05-18T19:04:38.837Z" }, 303 | { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527, upload-time = "2025-05-18T19:04:40.612Z" }, 304 | { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, 305 | ] 306 | 307 | [[package]] 308 | name = "lark" 309 | version = "1.2.2" 310 | source = { registry = "https://pypi.org/simple" } 311 | sdist = { url = "https://files.pythonhosted.org/packages/af/60/bc7622aefb2aee1c0b4ba23c1446d3e30225c8770b38d7aedbfb65ca9d5a/lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80", size = 252132, upload-time = "2024-08-13T19:49:00.652Z" } 312 | wheels = [ 313 | { url = "https://files.pythonhosted.org/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c", size = 111036, upload-time = "2024-08-13T19:48:58.603Z" }, 314 | ] 315 | 316 | [[package]] 317 | name = "markdown-it-py" 318 | version = "3.0.0" 319 | source = { registry = "https://pypi.org/simple" } 320 | dependencies = [ 321 | { name = "mdurl" }, 322 | ] 323 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } 324 | wheels = [ 325 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, 326 | ] 327 | 328 | [[package]] 329 | name = "mcp" 330 | version = "1.9.4" 331 | source = { registry = "https://pypi.org/simple" } 332 | dependencies = [ 333 | { name = "anyio" }, 334 | { name = "httpx" }, 335 | { name = "httpx-sse" }, 336 | { name = "pydantic" }, 337 | { name = "pydantic-settings" }, 338 | { name = "python-multipart" }, 339 | { name = "sse-starlette" }, 340 | { name = "starlette" }, 341 | { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, 342 | ] 343 | sdist = { url = "https://files.pythonhosted.org/packages/06/f2/dc2450e566eeccf92d89a00c3e813234ad58e2ba1e31d11467a09ac4f3b9/mcp-1.9.4.tar.gz", hash = "sha256:cfb0bcd1a9535b42edaef89947b9e18a8feb49362e1cc059d6e7fc636f2cb09f", size = 333294, upload-time = "2025-06-12T08:20:30.158Z" } 344 | wheels = [ 345 | { url = "https://files.pythonhosted.org/packages/97/fc/80e655c955137393c443842ffcc4feccab5b12fa7cb8de9ced90f90e6998/mcp-1.9.4-py3-none-any.whl", hash = "sha256:7fcf36b62936adb8e63f89346bccca1268eeca9bf6dfb562ee10b1dfbda9dac0", size = 130232, upload-time = "2025-06-12T08:20:28.551Z" }, 346 | ] 347 | 348 | [package.optional-dependencies] 349 | cli = [ 350 | { name = "python-dotenv" }, 351 | { name = "typer" }, 352 | ] 353 | 354 | [[package]] 355 | name = "mdurl" 356 | version = "0.1.2" 357 | source = { registry = "https://pypi.org/simple" } 358 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } 359 | wheels = [ 360 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, 361 | ] 362 | 363 | [[package]] 364 | name = "networkx" 365 | version = "3.5" 366 | source = { registry = "https://pypi.org/simple" } 367 | sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } 368 | wheels = [ 369 | { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, 370 | ] 371 | 372 | [[package]] 373 | name = "numpy" 374 | version = "2.3.1" 375 | source = { registry = "https://pypi.org/simple" } 376 | sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372, upload-time = "2025-06-21T12:28:33.469Z" } 377 | wheels = [ 378 | { url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381, upload-time = "2025-06-21T12:19:04.103Z" }, 379 | { url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726, upload-time = "2025-06-21T12:19:25.599Z" }, 380 | { url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145, upload-time = "2025-06-21T12:19:34.782Z" }, 381 | { url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409, upload-time = "2025-06-21T12:19:45.228Z" }, 382 | { url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630, upload-time = "2025-06-21T12:20:06.544Z" }, 383 | { url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546, upload-time = "2025-06-21T12:20:31.002Z" }, 384 | { url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538, upload-time = "2025-06-21T12:20:54.322Z" }, 385 | { url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327, upload-time = "2025-06-21T12:21:21.053Z" }, 386 | { url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330, upload-time = "2025-06-21T12:25:07.447Z" }, 387 | { url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565, upload-time = "2025-06-21T12:25:26.444Z" }, 388 | { url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262, upload-time = "2025-06-21T12:25:42.196Z" }, 389 | { url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593, upload-time = "2025-06-21T12:21:51.664Z" }, 390 | { url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523, upload-time = "2025-06-21T12:22:13.583Z" }, 391 | { url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993, upload-time = "2025-06-21T12:22:22.53Z" }, 392 | { url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652, upload-time = "2025-06-21T12:22:33.629Z" }, 393 | { url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561, upload-time = "2025-06-21T12:22:55.056Z" }, 394 | { url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349, upload-time = "2025-06-21T12:23:20.53Z" }, 395 | { url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053, upload-time = "2025-06-21T12:23:43.697Z" }, 396 | { url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184, upload-time = "2025-06-21T12:24:10.708Z" }, 397 | { url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678, upload-time = "2025-06-21T12:24:21.596Z" }, 398 | { url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697, upload-time = "2025-06-21T12:24:40.644Z" }, 399 | { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376, upload-time = "2025-06-21T12:24:56.884Z" }, 400 | ] 401 | 402 | [[package]] 403 | name = "openai" 404 | version = "1.109.1" 405 | source = { registry = "https://pypi.org/simple" } 406 | dependencies = [ 407 | { name = "anyio" }, 408 | { name = "distro" }, 409 | { name = "httpx" }, 410 | { name = "jiter" }, 411 | { name = "pydantic" }, 412 | { name = "sniffio" }, 413 | { name = "tqdm" }, 414 | { name = "typing-extensions" }, 415 | ] 416 | sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133, upload-time = "2025-09-24T13:00:53.075Z" } 417 | wheels = [ 418 | { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627, upload-time = "2025-09-24T13:00:50.754Z" }, 419 | ] 420 | 421 | [[package]] 422 | name = "openai-agents" 423 | version = "0.2.0" 424 | source = { registry = "https://pypi.org/simple" } 425 | dependencies = [ 426 | { name = "griffe" }, 427 | { name = "mcp" }, 428 | { name = "openai" }, 429 | { name = "pydantic" }, 430 | { name = "requests" }, 431 | { name = "types-requests" }, 432 | { name = "typing-extensions" }, 433 | ] 434 | sdist = { url = "https://files.pythonhosted.org/packages/5a/26/fd0bca3bae6c106c2996d2dcf4fbc0bad04d7bea61ae2bfae8ed631c4760/openai_agents-0.2.0.tar.gz", hash = "sha256:573734f220dcc6c2713bdc400e8ffea819e3ca9ce0e5d8f37fd077f740429e35", size = 1429855, upload-time = "2025-07-15T15:49:34.651Z" } 435 | wheels = [ 436 | { url = "https://files.pythonhosted.org/packages/8e/97/53a590cffedc6e13af8f196f681870d1fe187107f95458a8cf9b8fe25e4d/openai_agents-0.2.0-py3-none-any.whl", hash = "sha256:101f2d978f0ad715413f650aed00622fca06d9982a2e7f18bd0ab0a7132ba696", size = 156362, upload-time = "2025-07-15T15:49:33.229Z" }, 437 | ] 438 | 439 | [[package]] 440 | name = "pathspec" 441 | version = "0.12.1" 442 | source = { registry = "https://pypi.org/simple" } 443 | sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } 444 | wheels = [ 445 | { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, 446 | ] 447 | 448 | [[package]] 449 | name = "pyasn1" 450 | version = "0.6.1" 451 | source = { registry = "https://pypi.org/simple" } 452 | sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } 453 | wheels = [ 454 | { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, 455 | ] 456 | 457 | [[package]] 458 | name = "pyasn1-modules" 459 | version = "0.4.2" 460 | source = { registry = "https://pypi.org/simple" } 461 | dependencies = [ 462 | { name = "pyasn1" }, 463 | ] 464 | sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } 465 | wheels = [ 466 | { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, 467 | ] 468 | 469 | [[package]] 470 | name = "pydantic" 471 | version = "2.11.7" 472 | source = { registry = "https://pypi.org/simple" } 473 | dependencies = [ 474 | { name = "annotated-types" }, 475 | { name = "pydantic-core" }, 476 | { name = "typing-extensions" }, 477 | { name = "typing-inspection" }, 478 | ] 479 | sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } 480 | wheels = [ 481 | { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, 482 | ] 483 | 484 | [[package]] 485 | name = "pydantic-core" 486 | version = "2.33.2" 487 | source = { registry = "https://pypi.org/simple" } 488 | dependencies = [ 489 | { name = "typing-extensions" }, 490 | ] 491 | sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } 492 | wheels = [ 493 | { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, 494 | { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, 495 | { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, 496 | { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, 497 | { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, 498 | { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, 499 | { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, 500 | { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, 501 | { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, 502 | { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, 503 | { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, 504 | { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, 505 | { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, 506 | { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, 507 | { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, 508 | { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, 509 | { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, 510 | ] 511 | 512 | [[package]] 513 | name = "pydantic-settings" 514 | version = "2.10.0" 515 | source = { registry = "https://pypi.org/simple" } 516 | dependencies = [ 517 | { name = "pydantic" }, 518 | { name = "python-dotenv" }, 519 | { name = "typing-inspection" }, 520 | ] 521 | sdist = { url = "https://files.pythonhosted.org/packages/c2/ef/3d61472b7801c896f9efd9bb8750977d9577098b05224c5c41820690155e/pydantic_settings-2.10.0.tar.gz", hash = "sha256:7a12e0767ba283954f3fd3fefdd0df3af21b28aa849c40c35811d52d682fa876", size = 172625, upload-time = "2025-06-21T13:56:55.898Z" } 522 | wheels = [ 523 | { url = "https://files.pythonhosted.org/packages/7d/9e/fce9331fecf1d2761ff0516c5dceab8a5fd415e82943e727dc4c5fa84a90/pydantic_settings-2.10.0-py3-none-any.whl", hash = "sha256:33781dfa1c7405d5ed2b6f150830a93bb58462a847357bd8f162f8bacb77c027", size = 45232, upload-time = "2025-06-21T13:56:53.682Z" }, 524 | ] 525 | 526 | [[package]] 527 | name = "pygments" 528 | version = "2.19.2" 529 | source = { registry = "https://pypi.org/simple" } 530 | sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } 531 | wheels = [ 532 | { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, 533 | ] 534 | 535 | [[package]] 536 | name = "python-dotenv" 537 | version = "1.1.0" 538 | source = { registry = "https://pypi.org/simple" } 539 | sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" } 540 | wheels = [ 541 | { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, 542 | ] 543 | 544 | [[package]] 545 | name = "python-hcl2" 546 | version = "7.2.1" 547 | source = { registry = "https://pypi.org/simple" } 548 | dependencies = [ 549 | { name = "lark" }, 550 | ] 551 | sdist = { url = "https://files.pythonhosted.org/packages/e6/ac/2897618a086ccef9e78389ce58cc28a38b058b5a211c29e2fd4ebf0324ff/python_hcl2-7.2.1.tar.gz", hash = "sha256:bb5aad9e157ef65551a1fda0c08b170127b88caa416151c968b9688dbe99153d", size = 34065, upload-time = "2025-05-16T12:38:58.401Z" } 552 | wheels = [ 553 | { url = "https://files.pythonhosted.org/packages/91/61/ccc7b37372ded640bfc76ec3c777b54611ca0eae889422272297e97cda22/python_hcl2-7.2.1-py3-none-any.whl", hash = "sha256:cf54115c7f9878f0666b69ce692bca0b42262e6984b8838216251248c82bf838", size = 21821, upload-time = "2025-05-16T12:38:57.426Z" }, 554 | ] 555 | 556 | [[package]] 557 | name = "python-multipart" 558 | version = "0.0.20" 559 | source = { registry = "https://pypi.org/simple" } 560 | sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } 561 | wheels = [ 562 | { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, 563 | ] 564 | 565 | [[package]] 566 | name = "pyyaml" 567 | version = "6.0.2" 568 | source = { registry = "https://pypi.org/simple" } 569 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } 570 | wheels = [ 571 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, 572 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, 573 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, 574 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, 575 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, 576 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, 577 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, 578 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, 579 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, 580 | ] 581 | 582 | [[package]] 583 | name = "redis" 584 | version = "6.2.0" 585 | source = { registry = "https://pypi.org/simple" } 586 | sdist = { url = "https://files.pythonhosted.org/packages/ea/9a/0551e01ba52b944f97480721656578c8a7c46b51b99d66814f85fe3a4f3e/redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977", size = 4639129, upload-time = "2025-05-28T05:01:18.91Z" } 587 | wheels = [ 588 | { url = "https://files.pythonhosted.org/packages/13/67/e60968d3b0e077495a8fee89cf3f2373db98e528288a48f1ee44967f6e8c/redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e", size = 278659, upload-time = "2025-05-28T05:01:16.955Z" }, 589 | ] 590 | 591 | [[package]] 592 | name = "regex" 593 | version = "2024.11.6" 594 | source = { registry = "https://pypi.org/simple" } 595 | sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } 596 | wheels = [ 597 | { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525, upload-time = "2024-11-06T20:10:45.19Z" }, 598 | { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324, upload-time = "2024-11-06T20:10:47.177Z" }, 599 | { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617, upload-time = "2024-11-06T20:10:49.312Z" }, 600 | { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023, upload-time = "2024-11-06T20:10:51.102Z" }, 601 | { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072, upload-time = "2024-11-06T20:10:52.926Z" }, 602 | { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130, upload-time = "2024-11-06T20:10:54.828Z" }, 603 | { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857, upload-time = "2024-11-06T20:10:56.634Z" }, 604 | { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006, upload-time = "2024-11-06T20:10:59.369Z" }, 605 | { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650, upload-time = "2024-11-06T20:11:02.042Z" }, 606 | { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545, upload-time = "2024-11-06T20:11:03.933Z" }, 607 | { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045, upload-time = "2024-11-06T20:11:06.497Z" }, 608 | { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182, upload-time = "2024-11-06T20:11:09.06Z" }, 609 | { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733, upload-time = "2024-11-06T20:11:11.256Z" }, 610 | { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122, upload-time = "2024-11-06T20:11:13.161Z" }, 611 | { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545, upload-time = "2024-11-06T20:11:15Z" }, 612 | ] 613 | 614 | [[package]] 615 | name = "requests" 616 | version = "2.32.4" 617 | source = { registry = "https://pypi.org/simple" } 618 | dependencies = [ 619 | { name = "certifi" }, 620 | { name = "charset-normalizer" }, 621 | { name = "idna" }, 622 | { name = "urllib3" }, 623 | ] 624 | sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } 625 | wheels = [ 626 | { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, 627 | ] 628 | 629 | [[package]] 630 | name = "rich" 631 | version = "14.0.0" 632 | source = { registry = "https://pypi.org/simple" } 633 | dependencies = [ 634 | { name = "markdown-it-py" }, 635 | { name = "pygments" }, 636 | ] 637 | sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } 638 | wheels = [ 639 | { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, 640 | ] 641 | 642 | [[package]] 643 | name = "rsa" 644 | version = "4.9.1" 645 | source = { registry = "https://pypi.org/simple" } 646 | dependencies = [ 647 | { name = "pyasn1" }, 648 | ] 649 | sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } 650 | wheels = [ 651 | { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, 652 | ] 653 | 654 | [[package]] 655 | name = "scipy" 656 | version = "1.16.0" 657 | source = { registry = "https://pypi.org/simple" } 658 | dependencies = [ 659 | { name = "numpy" }, 660 | ] 661 | sdist = { url = "https://files.pythonhosted.org/packages/81/18/b06a83f0c5ee8cddbde5e3f3d0bb9b702abfa5136ef6d4620ff67df7eee5/scipy-1.16.0.tar.gz", hash = "sha256:b5ef54021e832869c8cfb03bc3bf20366cbcd426e02a58e8a58d7584dfbb8f62", size = 30581216, upload-time = "2025-06-22T16:27:55.782Z" } 662 | wheels = [ 663 | { url = "https://files.pythonhosted.org/packages/46/95/0746417bc24be0c2a7b7563946d61f670a3b491b76adede420e9d173841f/scipy-1.16.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:e9f414cbe9ca289a73e0cc92e33a6a791469b6619c240aa32ee18abdce8ab451", size = 36418162, upload-time = "2025-06-22T16:19:56.3Z" }, 664 | { url = "https://files.pythonhosted.org/packages/19/5a/914355a74481b8e4bbccf67259bbde171348a3f160b67b4945fbc5f5c1e5/scipy-1.16.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:bbba55fb97ba3cdef9b1ee973f06b09d518c0c7c66a009c729c7d1592be1935e", size = 28465985, upload-time = "2025-06-22T16:20:01.238Z" }, 665 | { url = "https://files.pythonhosted.org/packages/58/46/63477fc1246063855969cbefdcee8c648ba4b17f67370bd542ba56368d0b/scipy-1.16.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:58e0d4354eacb6004e7aa1cd350e5514bd0270acaa8d5b36c0627bb3bb486974", size = 20737961, upload-time = "2025-06-22T16:20:05.913Z" }, 666 | { url = "https://files.pythonhosted.org/packages/93/86/0fbb5588b73555e40f9d3d6dde24ee6fac7d8e301a27f6f0cab9d8f66ff2/scipy-1.16.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:75b2094ec975c80efc273567436e16bb794660509c12c6a31eb5c195cbf4b6dc", size = 23377941, upload-time = "2025-06-22T16:20:10.668Z" }, 667 | { url = "https://files.pythonhosted.org/packages/ca/80/a561f2bf4c2da89fa631b3cbf31d120e21ea95db71fd9ec00cb0247c7a93/scipy-1.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b65d232157a380fdd11a560e7e21cde34fdb69d65c09cb87f6cc024ee376351", size = 33196703, upload-time = "2025-06-22T16:20:16.097Z" }, 668 | { url = "https://files.pythonhosted.org/packages/11/6b/3443abcd0707d52e48eb315e33cc669a95e29fc102229919646f5a501171/scipy-1.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d8747f7736accd39289943f7fe53a8333be7f15a82eea08e4afe47d79568c32", size = 35083410, upload-time = "2025-06-22T16:20:21.734Z" }, 669 | { url = "https://files.pythonhosted.org/packages/20/ab/eb0fc00e1e48961f1bd69b7ad7e7266896fe5bad4ead91b5fc6b3561bba4/scipy-1.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eb9f147a1b8529bb7fec2a85cf4cf42bdfadf9e83535c309a11fdae598c88e8b", size = 35387829, upload-time = "2025-06-22T16:20:27.548Z" }, 670 | { url = "https://files.pythonhosted.org/packages/57/9e/d6fc64e41fad5d481c029ee5a49eefc17f0b8071d636a02ceee44d4a0de2/scipy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d2b83c37edbfa837a8923d19c749c1935ad3d41cf196006a24ed44dba2ec4358", size = 37841356, upload-time = "2025-06-22T16:20:35.112Z" }, 671 | { url = "https://files.pythonhosted.org/packages/7c/a7/4c94bbe91f12126b8bf6709b2471900577b7373a4fd1f431f28ba6f81115/scipy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:79a3c13d43c95aa80b87328a46031cf52508cf5f4df2767602c984ed1d3c6bbe", size = 38403710, upload-time = "2025-06-22T16:21:54.473Z" }, 672 | { url = "https://files.pythonhosted.org/packages/47/20/965da8497f6226e8fa90ad3447b82ed0e28d942532e92dd8b91b43f100d4/scipy-1.16.0-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:f91b87e1689f0370690e8470916fe1b2308e5b2061317ff76977c8f836452a47", size = 36813833, upload-time = "2025-06-22T16:20:43.925Z" }, 673 | { url = "https://files.pythonhosted.org/packages/28/f4/197580c3dac2d234e948806e164601c2df6f0078ed9f5ad4a62685b7c331/scipy-1.16.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:88a6ca658fb94640079e7a50b2ad3b67e33ef0f40e70bdb7dc22017dae73ac08", size = 28974431, upload-time = "2025-06-22T16:20:51.302Z" }, 674 | { url = "https://files.pythonhosted.org/packages/8a/fc/e18b8550048d9224426e76906694c60028dbdb65d28b1372b5503914b89d/scipy-1.16.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:ae902626972f1bd7e4e86f58fd72322d7f4ec7b0cfc17b15d4b7006efc385176", size = 21246454, upload-time = "2025-06-22T16:20:57.276Z" }, 675 | { url = "https://files.pythonhosted.org/packages/8c/48/07b97d167e0d6a324bfd7484cd0c209cc27338b67e5deadae578cf48e809/scipy-1.16.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:8cb824c1fc75ef29893bc32b3ddd7b11cf9ab13c1127fe26413a05953b8c32ed", size = 23772979, upload-time = "2025-06-22T16:21:03.363Z" }, 676 | { url = "https://files.pythonhosted.org/packages/4c/4f/9efbd3f70baf9582edf271db3002b7882c875ddd37dc97f0f675ad68679f/scipy-1.16.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:de2db7250ff6514366a9709c2cba35cb6d08498e961cba20d7cff98a7ee88938", size = 33341972, upload-time = "2025-06-22T16:21:11.14Z" }, 677 | { url = "https://files.pythonhosted.org/packages/3f/dc/9e496a3c5dbe24e76ee24525155ab7f659c20180bab058ef2c5fa7d9119c/scipy-1.16.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e85800274edf4db8dd2e4e93034f92d1b05c9421220e7ded9988b16976f849c1", size = 35185476, upload-time = "2025-06-22T16:21:19.156Z" }, 678 | { url = "https://files.pythonhosted.org/packages/ce/b3/21001cff985a122ba434c33f2c9d7d1dc3b669827e94f4fc4e1fe8b9dfd8/scipy-1.16.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4f720300a3024c237ace1cb11f9a84c38beb19616ba7c4cdcd771047a10a1706", size = 35570990, upload-time = "2025-06-22T16:21:27.797Z" }, 679 | { url = "https://files.pythonhosted.org/packages/e5/d3/7ba42647d6709251cdf97043d0c107e0317e152fa2f76873b656b509ff55/scipy-1.16.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:aad603e9339ddb676409b104c48a027e9916ce0d2838830691f39552b38a352e", size = 37950262, upload-time = "2025-06-22T16:21:36.976Z" }, 680 | { url = "https://files.pythonhosted.org/packages/eb/c4/231cac7a8385394ebbbb4f1ca662203e9d8c332825ab4f36ffc3ead09a42/scipy-1.16.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f56296fefca67ba605fd74d12f7bd23636267731a72cb3947963e76b8c0a25db", size = 38515076, upload-time = "2025-06-22T16:21:45.694Z" }, 681 | ] 682 | 683 | [[package]] 684 | name = "search-tools" 685 | version = "0.1.0" 686 | source = { virtual = "." } 687 | dependencies = [ 688 | { name = "cased-kit" }, 689 | { name = "mcp", extra = ["cli"] }, 690 | { name = "networkx" }, 691 | { name = "openai-agents" }, 692 | { name = "scipy" }, 693 | ] 694 | 695 | [package.metadata] 696 | requires-dist = [ 697 | { name = "cased-kit" }, 698 | { name = "mcp", extras = ["cli"], specifier = ">=1.9.4" }, 699 | { name = "networkx", specifier = ">=3.5" }, 700 | { name = "openai-agents", specifier = ">=0.2.0" }, 701 | { name = "scipy", specifier = ">=1.16.0" }, 702 | ] 703 | 704 | [[package]] 705 | name = "shellingham" 706 | version = "1.5.4" 707 | source = { registry = "https://pypi.org/simple" } 708 | sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } 709 | wheels = [ 710 | { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, 711 | ] 712 | 713 | [[package]] 714 | name = "sniffio" 715 | version = "1.3.1" 716 | source = { registry = "https://pypi.org/simple" } 717 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } 718 | wheels = [ 719 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, 720 | ] 721 | 722 | [[package]] 723 | name = "sse-starlette" 724 | version = "2.3.6" 725 | source = { registry = "https://pypi.org/simple" } 726 | dependencies = [ 727 | { name = "anyio" }, 728 | ] 729 | sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284, upload-time = "2025-05-30T13:34:12.914Z" } 730 | wheels = [ 731 | { url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606, upload-time = "2025-05-30T13:34:11.703Z" }, 732 | ] 733 | 734 | [[package]] 735 | name = "starlette" 736 | version = "0.46.2" 737 | source = { registry = "https://pypi.org/simple" } 738 | dependencies = [ 739 | { name = "anyio" }, 740 | ] 741 | sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846, upload-time = "2025-04-13T13:56:17.942Z" } 742 | wheels = [ 743 | { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" }, 744 | ] 745 | 746 | [[package]] 747 | name = "tenacity" 748 | version = "8.5.0" 749 | source = { registry = "https://pypi.org/simple" } 750 | sdist = { url = "https://files.pythonhosted.org/packages/a3/4d/6a19536c50b849338fcbe9290d562b52cbdcf30d8963d3588a68a4107df1/tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78", size = 47309, upload-time = "2024-07-05T07:25:31.836Z" } 751 | wheels = [ 752 | { url = "https://files.pythonhosted.org/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687", size = 28165, upload-time = "2024-07-05T07:25:29.591Z" }, 753 | ] 754 | 755 | [[package]] 756 | name = "tiktoken" 757 | version = "0.9.0" 758 | source = { registry = "https://pypi.org/simple" } 759 | dependencies = [ 760 | { name = "regex" }, 761 | { name = "requests" }, 762 | ] 763 | sdist = { url = "https://files.pythonhosted.org/packages/ea/cf/756fedf6981e82897f2d570dd25fa597eb3f4459068ae0572d7e888cfd6f/tiktoken-0.9.0.tar.gz", hash = "sha256:d02a5ca6a938e0490e1ff957bc48c8b078c88cb83977be1625b1fd8aac792c5d", size = 35991, upload-time = "2025-02-14T06:03:01.003Z" } 764 | wheels = [ 765 | { url = "https://files.pythonhosted.org/packages/7a/11/09d936d37f49f4f494ffe660af44acd2d99eb2429d60a57c71318af214e0/tiktoken-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b0e8e05a26eda1249e824156d537015480af7ae222ccb798e5234ae0285dbdb", size = 1064919, upload-time = "2025-02-14T06:02:37.494Z" }, 766 | { url = "https://files.pythonhosted.org/packages/80/0e/f38ba35713edb8d4197ae602e80837d574244ced7fb1b6070b31c29816e0/tiktoken-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27d457f096f87685195eea0165a1807fae87b97b2161fe8c9b1df5bd74ca6f63", size = 1007877, upload-time = "2025-02-14T06:02:39.516Z" }, 767 | { url = "https://files.pythonhosted.org/packages/fe/82/9197f77421e2a01373e27a79dd36efdd99e6b4115746ecc553318ecafbf0/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf8ded49cddf825390e36dd1ad35cd49589e8161fdcb52aa25f0583e90a3e01", size = 1140095, upload-time = "2025-02-14T06:02:41.791Z" }, 768 | { url = "https://files.pythonhosted.org/packages/f2/bb/4513da71cac187383541facd0291c4572b03ec23c561de5811781bbd988f/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc156cb314119a8bb9748257a2eaebd5cc0753b6cb491d26694ed42fc7cb3139", size = 1195649, upload-time = "2025-02-14T06:02:43Z" }, 769 | { url = "https://files.pythonhosted.org/packages/fa/5c/74e4c137530dd8504e97e3a41729b1103a4ac29036cbfd3250b11fd29451/tiktoken-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd69372e8c9dd761f0ab873112aba55a0e3e506332dd9f7522ca466e817b1b7a", size = 1258465, upload-time = "2025-02-14T06:02:45.046Z" }, 770 | { url = "https://files.pythonhosted.org/packages/de/a8/8f499c179ec900783ffe133e9aab10044481679bb9aad78436d239eee716/tiktoken-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ea0edb6f83dc56d794723286215918c1cde03712cbbafa0348b33448faf5b95", size = 894669, upload-time = "2025-02-14T06:02:47.341Z" }, 771 | ] 772 | 773 | [[package]] 774 | name = "tqdm" 775 | version = "4.67.1" 776 | source = { registry = "https://pypi.org/simple" } 777 | dependencies = [ 778 | { name = "colorama", marker = "sys_platform == 'win32'" }, 779 | ] 780 | sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } 781 | wheels = [ 782 | { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, 783 | ] 784 | 785 | [[package]] 786 | name = "tree-sitter" 787 | version = "0.24.0" 788 | source = { registry = "https://pypi.org/simple" } 789 | sdist = { url = "https://files.pythonhosted.org/packages/a7/a2/698b9d31d08ad5558f8bfbfe3a0781bd4b1f284e89bde3ad18e05101a892/tree-sitter-0.24.0.tar.gz", hash = "sha256:abd95af65ca2f4f7eca356343391ed669e764f37748b5352946f00f7fc78e734", size = 168304, upload-time = "2025-01-17T05:06:38.115Z" } 790 | wheels = [ 791 | { url = "https://files.pythonhosted.org/packages/61/cd/2348339c85803330ce38cee1c6cbbfa78a656b34ff58606ebaf5c9e83bd0/tree_sitter-0.24.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d4a6416ed421c4210f0ca405a4834d5ccfbb8ad6692d4d74f7773ef68f92071", size = 140781, upload-time = "2025-01-17T05:06:22.82Z" }, 792 | { url = "https://files.pythonhosted.org/packages/8b/a3/1ea9d8b64e8dcfcc0051028a9c84a630301290995cd6e947bf88267ef7b1/tree_sitter-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0992d483677e71d5c5d37f30dfb2e3afec2f932a9c53eec4fca13869b788c6c", size = 133928, upload-time = "2025-01-17T05:06:25.146Z" }, 793 | { url = "https://files.pythonhosted.org/packages/fe/ae/55c1055609c9428a4aedf4b164400ab9adb0b1bf1538b51f4b3748a6c983/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57277a12fbcefb1c8b206186068d456c600dbfbc3fd6c76968ee22614c5cd5ad", size = 564497, upload-time = "2025-01-17T05:06:27.53Z" }, 794 | { url = "https://files.pythonhosted.org/packages/ce/d0/f2ffcd04882c5aa28d205a787353130cbf84b2b8a977fd211bdc3b399ae3/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25fa22766d63f73716c6fec1a31ee5cf904aa429484256bd5fdf5259051ed74", size = 578917, upload-time = "2025-01-17T05:06:31.057Z" }, 795 | { url = "https://files.pythonhosted.org/packages/af/82/aebe78ea23a2b3a79324993d4915f3093ad1af43d7c2208ee90be9273273/tree_sitter-0.24.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d5d9537507e1c8c5fa9935b34f320bfec4114d675e028f3ad94f11cf9db37b9", size = 581148, upload-time = "2025-01-17T05:06:32.409Z" }, 796 | { url = "https://files.pythonhosted.org/packages/a1/b4/6b0291a590c2b0417cfdb64ccb8ea242f270a46ed429c641fbc2bfab77e0/tree_sitter-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:f58bb4956917715ec4d5a28681829a8dad5c342cafd4aea269f9132a83ca9b34", size = 120207, upload-time = "2025-01-17T05:06:34.841Z" }, 797 | { url = "https://files.pythonhosted.org/packages/a8/18/542fd844b75272630229c9939b03f7db232c71a9d82aadc59c596319ea6a/tree_sitter-0.24.0-cp313-cp313-win_arm64.whl", hash = "sha256:23641bd25dcd4bb0b6fa91b8fb3f46cc9f1c9f475efe4d536d3f1f688d1b84c8", size = 108232, upload-time = "2025-01-17T05:06:35.831Z" }, 798 | ] 799 | 800 | [[package]] 801 | name = "tree-sitter-c-sharp" 802 | version = "0.23.1" 803 | source = { registry = "https://pypi.org/simple" } 804 | sdist = { url = "https://files.pythonhosted.org/packages/22/85/a61c782afbb706a47d990eaee6977e7c2bd013771c5bf5c81c617684f286/tree_sitter_c_sharp-0.23.1.tar.gz", hash = "sha256:322e2cfd3a547a840375276b2aea3335fa6458aeac082f6c60fec3f745c967eb", size = 1317728, upload-time = "2024-11-11T05:25:32.535Z" } 805 | wheels = [ 806 | { url = "https://files.pythonhosted.org/packages/58/04/f6c2df4c53a588ccd88d50851155945cff8cd887bd70c175e00aaade7edf/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd", size = 372235, upload-time = "2024-11-11T05:25:19.424Z" }, 807 | { url = "https://files.pythonhosted.org/packages/99/10/1aa9486f1e28fc22810fa92cbdc54e1051e7f5536a5e5b5e9695f609b31e/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e", size = 419046, upload-time = "2024-11-11T05:25:20.679Z" }, 808 | { url = "https://files.pythonhosted.org/packages/0f/21/13df29f8fcb9ba9f209b7b413a4764b673dfd58989a0dd67e9c7e19e9c2e/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:986e93d845a438ec3c4416401aa98e6a6f6631d644bbbc2e43fcb915c51d255d", size = 415999, upload-time = "2024-11-11T05:25:22.359Z" }, 809 | { url = "https://files.pythonhosted.org/packages/ca/72/fc6846795bcdae2f8aa94cc8b1d1af33d634e08be63e294ff0d6794b1efc/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8024e466b2f5611c6dc90321f232d8584893c7fb88b75e4a831992f877616d2", size = 402830, upload-time = "2024-11-11T05:25:24.198Z" }, 810 | { url = "https://files.pythonhosted.org/packages/fe/3a/b6028c5890ce6653807d5fa88c72232c027c6ceb480dbeb3b186d60e5971/tree_sitter_c_sharp-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7f9bf876866835492281d336b9e1f9626ab668737f74e914c31d285261507da7", size = 397880, upload-time = "2024-11-11T05:25:25.937Z" }, 811 | { url = "https://files.pythonhosted.org/packages/47/d2/4facaa34b40f8104d8751746d0e1cd2ddf0beb9f1404b736b97f372bd1f3/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:ae9a9e859e8f44e2b07578d44f9a220d3fa25b688966708af6aa55d42abeebb3", size = 377562, upload-time = "2024-11-11T05:25:27.539Z" }, 812 | { url = "https://files.pythonhosted.org/packages/d8/88/3cf6bd9959d94d1fec1e6a9c530c5f08ff4115a474f62aedb5fedb0f7241/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:c81548347a93347be4f48cb63ec7d60ef4b0efa91313330e69641e49aa5a08c5", size = 375157, upload-time = "2024-11-11T05:25:30.839Z" }, 813 | ] 814 | 815 | [[package]] 816 | name = "tree-sitter-embedded-template" 817 | version = "0.23.2" 818 | source = { registry = "https://pypi.org/simple" } 819 | sdist = { url = "https://files.pythonhosted.org/packages/28/d6/5a58ea2f0480f5ed188b733114a8c275532a2fd1568b3898793b13d28af5/tree_sitter_embedded_template-0.23.2.tar.gz", hash = "sha256:7b24dcf2e92497f54323e617564d36866230a8bfb719dbb7b45b461510dcddaa", size = 8471, upload-time = "2024-11-11T06:54:05.5Z" } 820 | wheels = [ 821 | { url = "https://files.pythonhosted.org/packages/ef/c1/be0c48ed9609b720e74ade86f24ea086e353fe9c7405ee9630c3d52d09a2/tree_sitter_embedded_template-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:a505c2d2494464029d79db541cab52f6da5fb326bf3d355e69bf98b84eb89ae0", size = 9554, upload-time = "2024-11-11T06:53:58Z" }, 822 | { url = "https://files.pythonhosted.org/packages/6d/a5/7c12f5d302525ee36d1eafc28a68e4454da5bad208436d547326bee4ed76/tree_sitter_embedded_template-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:28028b93b42cc3753261ae7ce066675d407f59de512417524f9c3ab7792b1d37", size = 10051, upload-time = "2024-11-11T06:53:59.346Z" }, 823 | { url = "https://files.pythonhosted.org/packages/cd/87/95aaba8b64b849200bd7d4ae510cc394ecaef46a031499cbff301766970d/tree_sitter_embedded_template-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec399d59ce93ffb60759a2d96053eed529f3c3f6a27128f261710d0d0de60e10", size = 17532, upload-time = "2024-11-11T06:54:00.053Z" }, 824 | { url = "https://files.pythonhosted.org/packages/13/f8/8c837b898f00b35f9f3f76a4abc525e80866a69343083c9ff329e17ecb03/tree_sitter_embedded_template-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcfa01f62b88d50dbcb736cc23baec8ddbfe08daacfdc613eee8c04ab65efd09", size = 17394, upload-time = "2024-11-11T06:54:00.841Z" }, 825 | { url = "https://files.pythonhosted.org/packages/89/9b/893adf9e465d2d7f14870871bf2f3b30045e5ac417cb596f667a72eda493/tree_sitter_embedded_template-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6debd24791466f887109a433c31aa4a5deeba2b217817521c745a4e748a944ed", size = 16439, upload-time = "2024-11-11T06:54:02.214Z" }, 826 | { url = "https://files.pythonhosted.org/packages/40/96/e79934572723673db9f867000500c6eea61a37705e02c7aee9ee031bbb6f/tree_sitter_embedded_template-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:158fecb38be5b15db0190ef7238e5248f24bf32ae3cab93bc1197e293a5641eb", size = 12572, upload-time = "2024-11-11T06:54:03.481Z" }, 827 | { url = "https://files.pythonhosted.org/packages/63/06/27f678b9874e4e2e39ddc6f5cce3374c8c60e6046ea8588a491ab6fc9fcb/tree_sitter_embedded_template-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:9f1f3b79fe273f3d15a5b64c85fc6ebfb48decfbe8542accd05f5b7694860df0", size = 11232, upload-time = "2024-11-11T06:54:04.799Z" }, 828 | ] 829 | 830 | [[package]] 831 | name = "tree-sitter-language-pack" 832 | version = "0.8.0" 833 | source = { registry = "https://pypi.org/simple" } 834 | dependencies = [ 835 | { name = "tree-sitter" }, 836 | { name = "tree-sitter-c-sharp" }, 837 | { name = "tree-sitter-embedded-template" }, 838 | { name = "tree-sitter-yaml" }, 839 | ] 840 | sdist = { url = "https://files.pythonhosted.org/packages/93/b7/1272925d5cccd0c7a79df85fdc1a728a9cd9536adca10c473a86ea6a1022/tree_sitter_language_pack-0.8.0.tar.gz", hash = "sha256:49aafe322eb59ef4d4457577210fb20c18c5535b1a42b8e753aa699ed3bf9eed", size = 43693098, upload-time = "2025-06-08T13:19:05.653Z" } 841 | wheels = [ 842 | { url = "https://files.pythonhosted.org/packages/2e/44/f7d3c4c5e075de1b3ad9e7d006f2057d65d39d5a573d6ee72b1a7f3f6cd1/tree_sitter_language_pack-0.8.0-cp39-abi3-macosx_10_13_universal2.whl", hash = "sha256:7ab5dd0e4383bd0c845c153f65da62df035591fc79759a5f6efd5b27aaa551c5", size = 28609869, upload-time = "2025-06-08T13:18:54.966Z" }, 843 | { url = "https://files.pythonhosted.org/packages/bf/24/86f32fae7eaaf829cfd0013f8173fb0f3e75f6e0a8bc58bd165c821e17de/tree_sitter_language_pack-0.8.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:1757c04af8350ffdfd5509951fb7874dc1947604d6d9f16a2f88a0cd4fcc54cb", size = 17871704, upload-time = "2025-06-08T13:18:58.17Z" }, 844 | { url = "https://files.pythonhosted.org/packages/00/7d/9356ecb8d5fcc16e39154821226d0dc3662393b9f46326f539e3e71dc384/tree_sitter_language_pack-0.8.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:81aac45ddde6c7e9ac222d0157af03648b1382d4de3af321d1b913af96b796f0", size = 17729371, upload-time = "2025-06-08T13:19:00.421Z" }, 845 | { url = "https://files.pythonhosted.org/packages/19/49/cfe141b0be9e08aeb9e20f3a182e58b7af12a28f46949403005e5483afc6/tree_sitter_language_pack-0.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:e870a3cc067352b249393e887710dae4918c6454f7fd41e43108f3621a5f41f8", size = 14552212, upload-time = "2025-06-08T13:19:03.119Z" }, 846 | ] 847 | 848 | [[package]] 849 | name = "tree-sitter-yaml" 850 | version = "0.7.1" 851 | source = { registry = "https://pypi.org/simple" } 852 | sdist = { url = "https://files.pythonhosted.org/packages/0b/d0/97899f366e3d982ad92dd83faa2b1dd0060e5db99990e0d7f660902493f8/tree_sitter_yaml-0.7.1.tar.gz", hash = "sha256:2cea5f8d4ca4d10439bd7d9e458c61b330cb33cf7a92e4ef1d428e10e1ab7e2c", size = 91533, upload-time = "2025-05-22T13:34:57.257Z" } 853 | wheels = [ 854 | { url = "https://files.pythonhosted.org/packages/3f/7e/83a40de4315b8f9975d3fd562071bda8fa1dfc088b3359d048003f174fd0/tree_sitter_yaml-0.7.1-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:0256632914d6eb21819f21a85bab649505496ac01fac940eb08a410669346822", size = 43788, upload-time = "2025-05-22T13:34:49.261Z" }, 855 | { url = "https://files.pythonhosted.org/packages/ca/05/760b38e31f9ca1e8667cf82a07119956dcb865728f7d777a22f5ddf296c6/tree_sitter_yaml-0.7.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf9dd2649392e1f28a20f920f49acd9398cfb872876e338aa84562f8f868dc4d", size = 45001, upload-time = "2025-05-22T13:34:50.397Z" }, 856 | { url = "https://files.pythonhosted.org/packages/88/e9/6d8d502eeb96fb363c1ac926ac456afc55019836fc675263fd23754dfdc6/tree_sitter_yaml-0.7.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94eb8fcb1ac8e43f7da47e63880b6f283524460153f08420a167c1721e42b08a", size = 93852, upload-time = "2025-05-22T13:34:51.728Z" }, 857 | { url = "https://files.pythonhosted.org/packages/85/ef/b84bc6aaaa08022b4cc1d36212e837ce051306d50dd62993ffc21c9bf4ab/tree_sitter_yaml-0.7.1-cp310-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30410089828ebdece9abf3aa16b2e172b84cf2fd90a2b7d8022f6ed8cde90ecb", size = 92125, upload-time = "2025-05-22T13:34:52.731Z" }, 858 | { url = "https://files.pythonhosted.org/packages/16/0c/5caa26da012c93da1eadf66c6babb1b1e2e8dd4434668c7232739df87e46/tree_sitter_yaml-0.7.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:219af34f4b35b5c16f25426cc3f90cf725fbba17c9592f78504086e67787be09", size = 90443, upload-time = "2025-05-22T13:34:53.626Z" }, 859 | { url = "https://files.pythonhosted.org/packages/92/25/a14297ea2a575bc3c19fcf58a5983a926ad732c32af23a346d7fa0563d8d/tree_sitter_yaml-0.7.1-cp310-abi3-win_amd64.whl", hash = "sha256:550645223d68b7d6b4cfedf4972754724e64d369ec321fa33f57d3ca54cafc7c", size = 45517, upload-time = "2025-05-22T13:34:54.545Z" }, 860 | { url = "https://files.pythonhosted.org/packages/62/fa/b25e688df5b4e024bc3627bc3f951524ef9c8b0756f0646411efa5063a10/tree_sitter_yaml-0.7.1-cp310-abi3-win_arm64.whl", hash = "sha256:298ade69ad61f76bb3e50ced809650ec30521a51aa2708166b176419ccb0a6ba", size = 43801, upload-time = "2025-05-22T13:34:55.471Z" }, 861 | ] 862 | 863 | [[package]] 864 | name = "typer" 865 | version = "0.14.0" 866 | source = { registry = "https://pypi.org/simple" } 867 | dependencies = [ 868 | { name = "click" }, 869 | { name = "rich" }, 870 | { name = "shellingham" }, 871 | { name = "typing-extensions" }, 872 | ] 873 | sdist = { url = "https://files.pythonhosted.org/packages/0d/7e/24af5b9aaa0872f9f6dc5dcf789dc3e57ceb23b4c570b852cd4db0d98f14/typer-0.14.0.tar.gz", hash = "sha256:af58f737f8d0c0c37b9f955a6d39000b9ff97813afcbeef56af5e37cf743b45a", size = 98836, upload-time = "2024-11-28T22:48:42.11Z" } 874 | wheels = [ 875 | { url = "https://files.pythonhosted.org/packages/bb/d8/a3ab71d5587b42b832a7ef2e65b3e51a18f8da32b6ce169637d4d21995ed/typer-0.14.0-py3-none-any.whl", hash = "sha256:f476233a25770ab3e7b2eebf7c68f3bc702031681a008b20167573a4b7018f09", size = 44707, upload-time = "2024-11-28T22:48:40.29Z" }, 876 | ] 877 | 878 | [[package]] 879 | name = "types-requests" 880 | version = "2.32.4.20250913" 881 | source = { registry = "https://pypi.org/simple" } 882 | dependencies = [ 883 | { name = "urllib3" }, 884 | ] 885 | sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" } 886 | wheels = [ 887 | { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" }, 888 | ] 889 | 890 | [[package]] 891 | name = "typing-extensions" 892 | version = "4.14.0" 893 | source = { registry = "https://pypi.org/simple" } 894 | sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } 895 | wheels = [ 896 | { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, 897 | ] 898 | 899 | [[package]] 900 | name = "typing-inspection" 901 | version = "0.4.1" 902 | source = { registry = "https://pypi.org/simple" } 903 | dependencies = [ 904 | { name = "typing-extensions" }, 905 | ] 906 | sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } 907 | wheels = [ 908 | { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, 909 | ] 910 | 911 | [[package]] 912 | name = "urllib3" 913 | version = "2.5.0" 914 | source = { registry = "https://pypi.org/simple" } 915 | sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } 916 | wheels = [ 917 | { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, 918 | ] 919 | 920 | [[package]] 921 | name = "uvicorn" 922 | version = "0.34.3" 923 | source = { registry = "https://pypi.org/simple" } 924 | dependencies = [ 925 | { name = "click" }, 926 | { name = "h11" }, 927 | ] 928 | sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" } 929 | wheels = [ 930 | { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" }, 931 | ] 932 | 933 | [package.optional-dependencies] 934 | standard = [ 935 | { name = "colorama", marker = "sys_platform == 'win32'" }, 936 | { name = "httptools" }, 937 | { name = "python-dotenv" }, 938 | { name = "pyyaml" }, 939 | { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, 940 | { name = "watchfiles" }, 941 | { name = "websockets" }, 942 | ] 943 | 944 | [[package]] 945 | name = "uvloop" 946 | version = "0.21.0" 947 | source = { registry = "https://pypi.org/simple" } 948 | sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } 949 | wheels = [ 950 | { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123, upload-time = "2024-10-14T23:38:00.688Z" }, 951 | { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325, upload-time = "2024-10-14T23:38:02.309Z" }, 952 | { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806, upload-time = "2024-10-14T23:38:04.711Z" }, 953 | { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068, upload-time = "2024-10-14T23:38:06.385Z" }, 954 | { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428, upload-time = "2024-10-14T23:38:08.416Z" }, 955 | { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018, upload-time = "2024-10-14T23:38:10.888Z" }, 956 | ] 957 | 958 | [[package]] 959 | name = "watchfiles" 960 | version = "1.1.0" 961 | source = { registry = "https://pypi.org/simple" } 962 | dependencies = [ 963 | { name = "anyio" }, 964 | ] 965 | sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } 966 | wheels = [ 967 | { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, 968 | { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, 969 | { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, 970 | { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, 971 | { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, 972 | { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, 973 | { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, 974 | { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, 975 | { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, 976 | { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, 977 | { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, 978 | { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, 979 | { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, 980 | { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, 981 | { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, 982 | { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, 983 | { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, 984 | { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, 985 | { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, 986 | { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, 987 | { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, 988 | { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, 989 | { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, 990 | { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, 991 | { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, 992 | { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, 993 | { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, 994 | { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, 995 | { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, 996 | { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, 997 | { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, 998 | { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, 999 | { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, 1000 | { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, 1001 | { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, 1002 | { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, 1003 | { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, 1004 | { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, 1005 | { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, 1006 | { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, 1007 | { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, 1008 | { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, 1009 | { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "websockets" 1014 | version = "15.0.1" 1015 | source = { registry = "https://pypi.org/simple" } 1016 | sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } 1017 | wheels = [ 1018 | { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, 1019 | { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, 1020 | { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, 1021 | { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, 1022 | { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, 1023 | { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, 1024 | { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, 1025 | { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, 1026 | { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, 1027 | { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, 1028 | { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, 1029 | { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, 1030 | ] 1031 | --------------------------------------------------------------------------------