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.
- mcp_bridge/__init__.py +5 -0
- mcp_bridge/auth/__init__.py +32 -0
- mcp_bridge/auth/cli.py +208 -0
- mcp_bridge/auth/oauth.py +418 -0
- mcp_bridge/auth/openai_oauth.py +350 -0
- mcp_bridge/auth/token_store.py +195 -0
- mcp_bridge/config/__init__.py +14 -0
- mcp_bridge/config/hooks.py +174 -0
- mcp_bridge/prompts/__init__.py +18 -0
- mcp_bridge/prompts/delphi.py +110 -0
- mcp_bridge/prompts/dewey.py +183 -0
- mcp_bridge/prompts/document_writer.py +155 -0
- mcp_bridge/prompts/explore.py +118 -0
- mcp_bridge/prompts/frontend.py +112 -0
- mcp_bridge/prompts/multimodal.py +58 -0
- mcp_bridge/prompts/stravinsky.py +329 -0
- mcp_bridge/server.py +866 -0
- mcp_bridge/tools/__init__.py +31 -0
- mcp_bridge/tools/agent_manager.py +665 -0
- mcp_bridge/tools/background_tasks.py +166 -0
- mcp_bridge/tools/code_search.py +301 -0
- mcp_bridge/tools/continuous_loop.py +67 -0
- mcp_bridge/tools/lsp/__init__.py +29 -0
- mcp_bridge/tools/lsp/tools.py +526 -0
- mcp_bridge/tools/model_invoke.py +233 -0
- mcp_bridge/tools/project_context.py +141 -0
- mcp_bridge/tools/session_manager.py +302 -0
- mcp_bridge/tools/skill_loader.py +212 -0
- mcp_bridge/tools/task_runner.py +97 -0
- mcp_bridge/utils/__init__.py +1 -0
- stravinsky-0.1.12.dist-info/METADATA +198 -0
- stravinsky-0.1.12.dist-info/RECORD +34 -0
- stravinsky-0.1.12.dist-info/WHEEL +4 -0
- stravinsky-0.1.12.dist-info/entry_points.txt +3 -0
|
@@ -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,,
|