stravinsky 0.1.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of stravinsky might be problematic. Click here for more details.

@@ -0,0 +1,212 @@
1
+ """
2
+ Skill Loader - Claude Code Slash Command Discovery
3
+
4
+ Discovers and lists available skills (slash commands) from:
5
+ 1. Project-local .claude/commands/
6
+ 2. User-global ~/.claude/commands/
7
+
8
+ Skills are markdown files with frontmatter defining the command behavior.
9
+ """
10
+
11
+ import os
12
+ import re
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+
17
+ def parse_frontmatter(content: str) -> tuple[dict[str, Any], str]:
18
+ """
19
+ Parse YAML frontmatter from markdown content.
20
+
21
+ Returns:
22
+ Tuple of (metadata dict, body content)
23
+ """
24
+ if not content.startswith("---"):
25
+ return {}, content
26
+
27
+ # Find the closing ---
28
+ end_match = content.find("---", 3)
29
+ if end_match == -1:
30
+ return {}, content
31
+
32
+ frontmatter = content[3:end_match].strip()
33
+ body = content[end_match + 3:].strip()
34
+
35
+ # Simple YAML parsing for key: value pairs
36
+ metadata = {}
37
+ for line in frontmatter.split("\n"):
38
+ if ":" in line:
39
+ key, _, value = line.partition(":")
40
+ key = key.strip()
41
+ value = value.strip().strip('"').strip("'")
42
+ metadata[key] = value
43
+
44
+ return metadata, body
45
+
46
+
47
+ def discover_skills(project_path: str | None = None) -> list[dict[str, Any]]:
48
+ """
49
+ Discover available skills/commands.
50
+
51
+ Searches:
52
+ 1. Project-local: {project}/.claude/commands/
53
+ 2. User-global: ~/.claude/commands/
54
+
55
+ Args:
56
+ project_path: Project directory to search (defaults to cwd)
57
+
58
+ Returns:
59
+ List of skill definitions.
60
+ """
61
+ skills = []
62
+ search_paths = []
63
+
64
+ # Project-local commands
65
+ project = Path(project_path) if project_path else Path.cwd()
66
+ project_commands = project / ".claude" / "commands"
67
+ if project_commands.exists():
68
+ search_paths.append(("project", project_commands))
69
+
70
+ # User-global commands
71
+ user_commands = Path.home() / ".claude" / "commands"
72
+ if user_commands.exists():
73
+ search_paths.append(("user", user_commands))
74
+
75
+ for scope, commands_dir in search_paths:
76
+ for md_file in commands_dir.glob("*.md"):
77
+ try:
78
+ content = md_file.read_text()
79
+ metadata, body = parse_frontmatter(content)
80
+
81
+ skills.append({
82
+ "name": md_file.stem,
83
+ "scope": scope,
84
+ "path": str(md_file),
85
+ "description": metadata.get("description", ""),
86
+ "allowed_tools": metadata.get("allowed-tools", "").split(",") if metadata.get("allowed-tools") else [],
87
+ "body_preview": body[:200] + "..." if len(body) > 200 else body,
88
+ })
89
+ except Exception:
90
+ continue
91
+
92
+ return skills
93
+
94
+
95
+ def list_skills(project_path: str | None = None) -> str:
96
+ """
97
+ List all available skills for MCP tool.
98
+
99
+ Args:
100
+ project_path: Project directory to search
101
+
102
+ Returns:
103
+ Formatted skill listing.
104
+ """
105
+ skills = discover_skills(project_path)
106
+
107
+ if not skills:
108
+ return "No skills found. Create .claude/commands/*.md files to add skills."
109
+
110
+ lines = [f"Found {len(skills)} skill(s):\n"]
111
+
112
+ for skill in skills:
113
+ scope_badge = "[project]" if skill["scope"] == "project" else "[user]"
114
+ lines.append(f" /{skill['name']} {scope_badge}")
115
+ if skill["description"]:
116
+ lines.append(f" {skill['description']}")
117
+
118
+ return "\n".join(lines)
119
+
120
+
121
+ def get_skill(name: str, project_path: str | None = None) -> str:
122
+ """
123
+ Get the content of a specific skill.
124
+
125
+ Args:
126
+ name: Skill name (filename without .md)
127
+ project_path: Project directory to search
128
+
129
+ Returns:
130
+ Skill content or error message.
131
+ """
132
+ skills = discover_skills(project_path)
133
+
134
+ skill = next((s for s in skills if s["name"] == name), None)
135
+ if not skill:
136
+ available = ", ".join(s["name"] for s in skills)
137
+ return f"Skill '{name}' not found. Available: {available or 'none'}"
138
+
139
+ try:
140
+ content = Path(skill["path"]).read_text()
141
+ metadata, body = parse_frontmatter(content)
142
+
143
+ lines = [
144
+ f"## Skill: {name}",
145
+ f"**Scope**: {skill['scope']}",
146
+ f"**Path**: {skill['path']}",
147
+ ]
148
+
149
+ if metadata.get("description"):
150
+ lines.append(f"**Description**: {metadata['description']}")
151
+
152
+ if metadata.get("allowed-tools"):
153
+ lines.append(f"**Allowed Tools**: {metadata['allowed-tools']}")
154
+
155
+ lines.extend(["", "---", "", body])
156
+
157
+ return "\n".join(lines)
158
+
159
+ except Exception as e:
160
+ return f"Error reading skill: {e}"
161
+
162
+
163
+ def create_skill(
164
+ name: str,
165
+ description: str,
166
+ content: str,
167
+ scope: str = "project",
168
+ project_path: str | None = None,
169
+ ) -> str:
170
+ """
171
+ Create a new skill file.
172
+
173
+ Args:
174
+ name: Skill name (will be used as filename)
175
+ description: Short description for frontmatter
176
+ content: Skill body content
177
+ scope: "project" or "user"
178
+ project_path: Project directory for project-scope skills
179
+
180
+ Returns:
181
+ Success or error message.
182
+ """
183
+ # Sanitize name
184
+ name = re.sub(r"[^a-zA-Z0-9_-]", "-", name.lower())
185
+
186
+ if scope == "project":
187
+ base_dir = Path(project_path) if project_path else Path.cwd()
188
+ commands_dir = base_dir / ".claude" / "commands"
189
+ else:
190
+ commands_dir = Path.home() / ".claude" / "commands"
191
+
192
+ # Ensure directory exists
193
+ commands_dir.mkdir(parents=True, exist_ok=True)
194
+
195
+ skill_path = commands_dir / f"{name}.md"
196
+
197
+ if skill_path.exists():
198
+ return f"Skill '{name}' already exists at {skill_path}"
199
+
200
+ # Create skill content
201
+ skill_content = f"""---
202
+ description: {description}
203
+ ---
204
+
205
+ {content}
206
+ """
207
+
208
+ try:
209
+ skill_path.write_text(skill_content)
210
+ return f"Created skill '{name}' at {skill_path}"
211
+ except Exception as e:
212
+ return f"Error creating skill: {e}"
@@ -0,0 +1,97 @@
1
+ """
2
+ Task Runner for Stravinsky background sub-agents.
3
+
4
+ This script is executed as a background process to handle agent tasks,
5
+ capture output, and update status in tasks.json.
6
+ """
7
+
8
+ import argparse
9
+ import json
10
+ import logging
11
+ import os
12
+ import sys
13
+ import asyncio
14
+ from datetime import datetime
15
+ from pathlib import Path
16
+
17
+ # Setup logging
18
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
19
+ logger = logging.getLogger("task_runner")
20
+
21
+ async def run_task(task_id: str, base_dir: str):
22
+ base_path = Path(base_dir)
23
+ tasks_file = base_path / "tasks.json"
24
+ agents_dir = base_path / "agents"
25
+
26
+ # Load task details
27
+ try:
28
+ with open(tasks_file, "r") as f:
29
+ tasks = json.load(f)
30
+ task = tasks.get(task_id)
31
+ except Exception as e:
32
+ logger.error(f"Failed to load tasks: {e}")
33
+ return
34
+
35
+ if not task:
36
+ logger.error(f"Task {task_id} not found")
37
+ return
38
+
39
+ prompt = task.get("prompt")
40
+ model = task.get("model", "gemini-2.0-flash")
41
+
42
+ output_file = agents_dir / f"{task_id}.out"
43
+ agents_dir.mkdir(parents=True, exist_ok=True)
44
+
45
+ try:
46
+ # Import model invocation
47
+ from mcp_bridge.tools.model_invoke import invoke_gemini
48
+
49
+ logger.info(f"Executing task {task_id} with model {model}...")
50
+
51
+ # Execute the model call
52
+ result = await invoke_gemini(prompt=prompt, model=model)
53
+
54
+ # Save result
55
+ with open(output_file, "w") as f:
56
+ f.write(result)
57
+
58
+ # Update status
59
+ with open(tasks_file, "r") as f:
60
+ tasks = json.load(f)
61
+
62
+ if task_id in tasks:
63
+ tasks[task_id].update({
64
+ "status": "completed",
65
+ "result": result,
66
+ "completed_at": datetime.now().isoformat()
67
+ })
68
+ with open(tasks_file, "w") as f:
69
+ json.dump(tasks, f, indent=2)
70
+
71
+ logger.info(f"Task {task_id} completed successfully")
72
+
73
+ except Exception as e:
74
+ logger.exception(f"Task {task_id} failed: {e}")
75
+
76
+ # Update status with error
77
+ try:
78
+ with open(tasks_file, "r") as f:
79
+ tasks = json.load(f)
80
+ if task_id in tasks:
81
+ tasks[task_id].update({
82
+ "status": "failed",
83
+ "error": str(e),
84
+ "completed_at": datetime.now().isoformat()
85
+ })
86
+ with open(tasks_file, "w") as f:
87
+ json.dump(tasks, f, indent=2)
88
+ except:
89
+ pass
90
+
91
+ if __name__ == "__main__":
92
+ parser = argparse.ArgumentParser()
93
+ parser.add_argument("--task-id", required=True)
94
+ parser.add_argument("--base-dir", required=True)
95
+ args = parser.parse_args()
96
+
97
+ asyncio.run(run_task(args.task_id, args.base_dir))
@@ -0,0 +1 @@
1
+ # Utility functions
@@ -0,0 +1,198 @@
1
+ Metadata-Version: 2.4
2
+ Name: stravinsky
3
+ Version: 0.1.12
4
+ Summary: MCP Bridge for Claude Code with Multi-Model Support. Install globally: claude mcp add --scope user stravinsky -- uvx stravinsky. Add to CLAUDE.md: See https://pypi.org/project/stravinsky/
5
+ Project-URL: Repository, https://github.com/GratefulDave/stravinsky
6
+ Project-URL: Issues, https://github.com/GratefulDave/stravinsky/issues
7
+ Author: Stravinsky Team
8
+ License: MIT
9
+ Keywords: claude,gemini,mcp,oauth,openai
10
+ Requires-Python: >=3.11
11
+ Requires-Dist: aiofiles>=23.1.0
12
+ Requires-Dist: cryptography>=41.0.0
13
+ Requires-Dist: google-auth-oauthlib>=1.0.0
14
+ Requires-Dist: google-auth>=2.20.0
15
+ Requires-Dist: httpx>=0.24.0
16
+ Requires-Dist: jedi>=0.19.2
17
+ Requires-Dist: keyring>=25.7.0
18
+ Requires-Dist: mcp>=1.0.0
19
+ Requires-Dist: openai>=1.0.0
20
+ Requires-Dist: psutil>=5.9.0
21
+ Requires-Dist: pydantic>=2.0.0
22
+ Requires-Dist: python-dotenv>=1.0.0
23
+ Requires-Dist: rich>=13.0.0
24
+ Requires-Dist: ruff>=0.14.10
25
+ Provides-Extra: dev
26
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
27
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ <div align="center">
33
+ <img src="https://raw.githubusercontent.com/GratefulDave/stravinsky/main/assets/logo.png" width="300" alt="Stravinsky Logo">
34
+ <h1>Stravinsky</h1>
35
+ <p><strong>The Avant-Garde MCP Bridge for Claude Code</strong></p>
36
+ <p><em>Movement • Rhythm • Precision</em></p>
37
+ </div>
38
+
39
+ ---
40
+
41
+ ## What is Stravinsky?
42
+
43
+ **Multi-model AI orchestration** with OAuth authentication for Claude Code.
44
+
45
+ ## Features
46
+
47
+ - 🔐 **OAuth Authentication** - Secure browser-based auth for Google (Gemini) and OpenAI (ChatGPT)
48
+ - 🤖 **Multi-Model Support** - Seamlessly invoke Gemini and GPT models from Claude
49
+ - 🛠️ **31 MCP Tools** - Model invocation, code search, LSP integrations, session management, and more
50
+ - 🧠 **7 Specialized Agents** - Stravinsky (orchestrator), Delphi (advisor), Dewey (documentation), and more
51
+ - 🔄 **Background Tasks** - Spawn parallel agents with full tool access via Claude Code CLI
52
+ - 📝 **LSP Integration** - Full Language Server Protocol support for Python (jedi)
53
+ - 🔍 **AST-Aware Search** - Structural code search and refactoring with ast-grep
54
+
55
+ ## Quick Start
56
+
57
+ ### Installation
58
+
59
+ **From PyPI (Recommended):**
60
+
61
+ ```bash
62
+ # One-shot with uvx - no installation needed!
63
+ claude mcp add stravinsky -- uvx stravinsky
64
+
65
+ # Or install globally first:
66
+ uv tool install stravinsky
67
+ claude mcp add stravinsky -- stravinsky
68
+ ```
69
+
70
+ **From Source (for development):**
71
+
72
+ ```bash
73
+ uv tool install --editable /path/to/stravinsky
74
+ claude mcp add stravinsky -- stravinsky
75
+ ```
76
+
77
+ ### Authentication
78
+
79
+ ```bash
80
+ # Login to Google (Gemini)
81
+ stravinsky-auth login gemini
82
+
83
+ # Login to OpenAI (ChatGPT Plus/Pro required)
84
+ stravinsky-auth login openai
85
+
86
+ # Check status
87
+ stravinsky-auth status
88
+
89
+ # Logout
90
+ stravinsky-auth logout gemini
91
+ ```
92
+
93
+ ## Add to Your CLAUDE.md
94
+
95
+ After installing globally, add this to your project's `CLAUDE.md`:
96
+
97
+ ```markdown
98
+ ## Stravinsky MCP (Parallel Agents)
99
+
100
+ Use Stravinsky MCP tools. **DEFAULT: spawn parallel agents for multi-step tasks.**
101
+
102
+ ### Agent Tools
103
+
104
+ - `agent_spawn(prompt, agent_type, description)` - Spawn background agent with full tool access
105
+ - `agent_output(task_id, block)` - Get results (block=True to wait)
106
+ - `agent_progress(task_id)` - Check real-time progress
107
+ - `agent_list()` - Overview of all running agents
108
+ - `agent_cancel(task_id)` - Stop a running agent
109
+
110
+ ### Agent Types
111
+
112
+ - `explore` - Codebase search, "where is X?" questions
113
+ - `dewey` - Documentation research, implementation examples
114
+ - `frontend` - UI/UX work, component design
115
+ - `delphi` - Strategic advice, architecture review
116
+
117
+ ### Parallel Execution (MANDATORY)
118
+
119
+ For ANY task with 2+ independent steps:
120
+
121
+ 1. **Immediately use agent_spawn** for each independent component
122
+ 2. Fire all agents simultaneously, don't wait
123
+ 3. Monitor with agent_progress, collect with agent_output
124
+
125
+ ### ULTRATHINK / ULTRAWORK
126
+
127
+ - **ULTRATHINK**: Engage exhaustive deep reasoning, multi-dimensional analysis
128
+ - **ULTRAWORK**: Maximum parallel execution - spawn agents aggressively for every subtask
129
+ ```
130
+
131
+ ## Tools (31)
132
+
133
+ | Category | Tools |
134
+ | ---------------- | ---------------------------------------------------------------------------------- |
135
+ | **Model Invoke** | `invoke_gemini`, `invoke_openai`, `get_system_health` |
136
+ | **Environment** | `get_project_context`, `task_spawn`, `task_status`, `task_list` |
137
+ | **Agents** | `agent_spawn`, `agent_output`, `agent_cancel`, `agent_list`, `agent_progress` |
138
+ | **Code Search** | `ast_grep_search`, `ast_grep_replace`, `grep_search`, `glob_files` |
139
+ | **LSP** | `lsp_diagnostics`, `lsp_hover`, `lsp_goto_definition`, `lsp_find_references`, etc. |
140
+ | **Sessions** | `session_list`, `session_read`, `session_search` |
141
+ | **Skills** | `skill_list`, `skill_get` |
142
+
143
+ ## Agent Prompts (7)
144
+
145
+ | Prompt | Purpose |
146
+ | ----------------- | ------------------------------------------------------------- |
147
+ | `stravinsky` | Task orchestration, planning, and goal-oriented execution. |
148
+ | `delphi` | Strategic technical advisor (GPT-based) for hard debugging. |
149
+ | `dewey` | Documentation and multi-repository research specialist. |
150
+ | `explore` | Specialized for codebase-wide search and structural analysis. |
151
+ | `frontend` | UI/UX Engineer (Gemini-optimized) for component prototyping. |
152
+ | `document_writer` | Technical documentation and specification writer. |
153
+ | `multimodal` | Visual analysis expert for UI screenshots and diagrams. |
154
+
155
+ ## Development
156
+
157
+ ```bash
158
+ # Install in development mode
159
+ uv pip install -e .
160
+
161
+ # Run server
162
+ stravinsky
163
+ ```
164
+
165
+ ## Project Structure
166
+
167
+ ```
168
+ stravinsky/
169
+ ├── mcp_bridge/ # Python MCP server
170
+ │ ├── server.py # Entry point
171
+ │ ├── auth/ # OAuth (Google & OpenAI)
172
+ │ ├── tools/ # Model invoke, search, skills
173
+ │ ├── prompts/ # Agent system prompts
174
+ │ └── config/ # Bridge configuration
175
+ ├── pyproject.toml # Build system
176
+ └── README.md # This file
177
+ ```
178
+
179
+ ## Troubleshooting
180
+
181
+ ### OpenAI "Port 1455 in use"
182
+
183
+ The Codex CLI uses the same port. Stop it with: `killall codex`
184
+
185
+ ### OpenAI Authentication Failed
186
+
187
+ - Ensure you have a ChatGPT Plus/Pro subscription
188
+ - Tokens expire occasionally; run `stravinsky-auth login openai` to refresh
189
+
190
+ ## License
191
+
192
+ MIT
193
+
194
+ ---
195
+
196
+ <div align="center">
197
+ <small>Built with obsession by the Google Deepmind team.</small>
198
+ </div>
@@ -0,0 +1,34 @@
1
+ mcp_bridge/__init__.py,sha256=8hnj-sTcl7-N11RY05wAGqveB_WtfEciYsxjQkXAnmc,156
2
+ mcp_bridge/server.py,sha256=3HbPqRCXN7N94pXh5MzElSN-sU9pHuuf0ntX4r3xOGA,34139
3
+ mcp_bridge/auth/__init__.py,sha256=AGHNtKzqvZYMLQ35Qg6aOabpxBqmkR-pjXv8Iby9oMw,797
4
+ mcp_bridge/auth/cli.py,sha256=z894Jb5buAtzFv8455JuzGeghT-cXphgyp82LryM_NU,6767
5
+ mcp_bridge/auth/oauth.py,sha256=lFTVYN6kjXWAqqYpg9L6XbWYI-Pg-_LFvc4WZAlzM9k,12450
6
+ mcp_bridge/auth/openai_oauth.py,sha256=0Ks2X-NXLCBzqs3xnbj9QLZpugICOX5qB5y5vtDENOo,11522
7
+ mcp_bridge/auth/token_store.py,sha256=3A6TZJ7Wju6QfhALeX4IMhY5jzb9OWMrDzwRbfAukiU,5650
8
+ mcp_bridge/config/__init__.py,sha256=uapHdrSWWrafVKD9CTB1J_7Dw0_RajRhoDGjy9zH21o,256
9
+ mcp_bridge/config/hooks.py,sha256=WvWC6ZUc8y1IXPlGCjLYAAsGGigd5tWeGiw585OGNwA,4624
10
+ mcp_bridge/prompts/__init__.py,sha256=pQmPxbdfS_S-ENB-VX33MXbgfGs8zHfLB_QOBtaPjis,322
11
+ mcp_bridge/prompts/delphi.py,sha256=OgsG5l9lsomj4rpohCtdU28ZW15gugCPG9eYwN340d4,4910
12
+ mcp_bridge/prompts/dewey.py,sha256=5-hRlOIJbfat9G_Lj-N-yhA7xk8c9lSOhK9yhSBqIs0,5261
13
+ mcp_bridge/prompts/document_writer.py,sha256=hiCbxgTU8HKPJkS0eNpPPtzSqDXPreApU2OqiS6zh-0,5618
14
+ mcp_bridge/prompts/explore.py,sha256=121R3bNFbb7AIte69eDtLfxdyHdOo3l16L6cUH1p-kk,3592
15
+ mcp_bridge/prompts/frontend.py,sha256=j91I8k5vcVed13eeX-Ebiv49x9Qj4HO_SQN1xhB8TLQ,4943
16
+ mcp_bridge/prompts/multimodal.py,sha256=Svw11N392LjshalasOd80X0Qw_qtOMqu_lD-_HmQDIo,1936
17
+ mcp_bridge/prompts/stravinsky.py,sha256=dhZnB_gv4mK1s_9kEVmWGbR7q-Pn8Z98exJD_PiYvYQ,12635
18
+ mcp_bridge/tools/__init__.py,sha256=YjNSlDzQ3xKezT-MlMfv3OXS9mlW86Xge0YBKpm1ACw,905
19
+ mcp_bridge/tools/agent_manager.py,sha256=OaRdaJOhkblwuIXTwVfoyboQ8g-8_nI1h5iqnYM6u4A,22274
20
+ mcp_bridge/tools/background_tasks.py,sha256=G1iQ00538q939-DJbzaEsA0uZI2TfwzawFCDx8QC5Hg,5178
21
+ mcp_bridge/tools/code_search.py,sha256=sR-alLQuxaXUFB9hby_wQsQu3Io644wdnpdOM_vm0aw,9978
22
+ mcp_bridge/tools/continuous_loop.py,sha256=MM3FnF3ULuR32h0tqJP8uF48iJg6R9dbyHy_36KLOls,2100
23
+ mcp_bridge/tools/model_invoke.py,sha256=1oXd8rIQShd5ZLV4OdH8C-cwM42c4dCzBxLvD3rnrro,7059
24
+ mcp_bridge/tools/project_context.py,sha256=bXKxuW1pGjtIbeNjMgpBoQL-d_CI94UPBVpRjUyhX20,4707
25
+ mcp_bridge/tools/session_manager.py,sha256=tCVLLvO-Kttla7OxPImb_NSGL_9aW46ilq5ej_IcnlA,9252
26
+ mcp_bridge/tools/skill_loader.py,sha256=6IhlEEPdNyLjqbtrPMnZdXz7u0KpGpR6EQkXykeC1VQ,6092
27
+ mcp_bridge/tools/task_runner.py,sha256=OCYAXy7vZTHf_np1IITv7azLlwomBYqYI9qu6oQna44,2888
28
+ mcp_bridge/tools/lsp/__init__.py,sha256=fLiII9qgeachI3MlkO6uGulfUH3T0YDeyEfO65bbxdw,549
29
+ mcp_bridge/tools/lsp/tools.py,sha256=d0gR8WuOjxLKWIbIXw2n1k6jUOrkoZW7C7TuA08OQwg,16225
30
+ mcp_bridge/utils/__init__.py,sha256=pbHV4nq5SLUYcAyTmLUZYrp293Ctud57X8hwsMGA_BM,20
31
+ stravinsky-0.1.12.dist-info/METADATA,sha256=dtLX88AW28vQNUgzx3nZka--SVrkC2esotJ6bvutY24,6890
32
+ stravinsky-0.1.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
33
+ stravinsky-0.1.12.dist-info/entry_points.txt,sha256=BISwF7i71Oen7jFVmBXz8fxiU11Cp415wPF0xXG2Q3s,97
34
+ stravinsky-0.1.12.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ stravinsky = mcp_bridge.server:main
3
+ stravinsky-auth = mcp_bridge.auth.cli:main