stravinsky 0.2.52__py3-none-any.whl → 0.4.18__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 +1 -1
- mcp_bridge/auth/token_store.py +113 -11
- mcp_bridge/cli/__init__.py +6 -0
- mcp_bridge/cli/install_hooks.py +1265 -0
- mcp_bridge/cli/session_report.py +585 -0
- mcp_bridge/config/MANIFEST_SCHEMA.md +305 -0
- mcp_bridge/config/README.md +276 -0
- mcp_bridge/config/hook_config.py +249 -0
- mcp_bridge/config/hooks_manifest.json +138 -0
- mcp_bridge/config/rate_limits.py +222 -0
- mcp_bridge/config/skills_manifest.json +128 -0
- mcp_bridge/hooks/HOOKS_SETTINGS.json +175 -0
- mcp_bridge/hooks/README.md +215 -0
- mcp_bridge/hooks/__init__.py +119 -60
- mcp_bridge/hooks/edit_recovery.py +42 -37
- mcp_bridge/hooks/git_noninteractive.py +89 -0
- mcp_bridge/hooks/keyword_detector.py +30 -0
- mcp_bridge/hooks/manager.py +8 -0
- mcp_bridge/hooks/notification_hook.py +103 -0
- mcp_bridge/hooks/parallel_execution.py +111 -0
- mcp_bridge/hooks/pre_compact.py +82 -183
- mcp_bridge/hooks/rules_injector.py +507 -0
- mcp_bridge/hooks/session_notifier.py +125 -0
- mcp_bridge/{native_hooks → hooks}/stravinsky_mode.py +51 -16
- mcp_bridge/hooks/subagent_stop.py +98 -0
- mcp_bridge/hooks/task_validator.py +73 -0
- mcp_bridge/hooks/tmux_manager.py +141 -0
- mcp_bridge/hooks/todo_continuation.py +90 -0
- mcp_bridge/hooks/todo_delegation.py +88 -0
- mcp_bridge/hooks/tool_messaging.py +267 -0
- mcp_bridge/hooks/truncator.py +21 -17
- mcp_bridge/notifications.py +151 -0
- mcp_bridge/prompts/multimodal.py +24 -3
- mcp_bridge/server.py +214 -49
- mcp_bridge/server_tools.py +445 -0
- mcp_bridge/tools/__init__.py +22 -18
- mcp_bridge/tools/agent_manager.py +220 -32
- mcp_bridge/tools/code_search.py +97 -11
- mcp_bridge/tools/lsp/__init__.py +7 -0
- mcp_bridge/tools/lsp/manager.py +448 -0
- mcp_bridge/tools/lsp/tools.py +637 -150
- mcp_bridge/tools/model_invoke.py +208 -106
- mcp_bridge/tools/query_classifier.py +323 -0
- mcp_bridge/tools/semantic_search.py +3042 -0
- mcp_bridge/tools/templates.py +32 -18
- mcp_bridge/update_manager.py +589 -0
- mcp_bridge/update_manager_pypi.py +299 -0
- stravinsky-0.4.18.dist-info/METADATA +468 -0
- stravinsky-0.4.18.dist-info/RECORD +88 -0
- stravinsky-0.4.18.dist-info/entry_points.txt +5 -0
- mcp_bridge/native_hooks/edit_recovery.py +0 -46
- mcp_bridge/native_hooks/todo_delegation.py +0 -54
- mcp_bridge/native_hooks/truncator.py +0 -23
- stravinsky-0.2.52.dist-info/METADATA +0 -204
- stravinsky-0.2.52.dist-info/RECORD +0 -63
- stravinsky-0.2.52.dist-info/entry_points.txt +0 -3
- /mcp_bridge/{native_hooks → hooks}/context.py +0 -0
- {stravinsky-0.2.52.dist-info → stravinsky-0.4.18.dist-info}/WHEEL +0 -0
mcp_bridge/hooks/pre_compact.py
CHANGED
|
@@ -1,224 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
1
2
|
"""
|
|
2
|
-
PreCompact
|
|
3
|
+
PreCompact hook: Context preservation before compaction.
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Fires before Claude Code compacts conversation context to:
|
|
6
|
+
1. Preserve critical context patterns
|
|
7
|
+
2. Maintain stravinsky mode state
|
|
8
|
+
3. Warn about information loss
|
|
9
|
+
4. Save state for recovery
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
Cannot block compaction (exit 2 only shows error).
|
|
9
12
|
"""
|
|
10
13
|
|
|
11
|
-
import
|
|
12
|
-
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from typing import List, Dict, Any
|
|
13
19
|
|
|
14
|
-
logger = logging.getLogger(__name__)
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
STRAVINSKY_MODE_FILE = Path.home() / ".stravinsky_mode"
|
|
22
|
+
STATE_DIR = Path.home() / ".claude" / "state"
|
|
23
|
+
COMPACTION_LOG = STATE_DIR / "compaction.jsonl"
|
|
18
24
|
|
|
19
|
-
#
|
|
25
|
+
# Patterns to preserve
|
|
20
26
|
PRESERVE_PATTERNS = [
|
|
21
|
-
# Architecture decisions
|
|
22
27
|
"ARCHITECTURE:",
|
|
23
28
|
"DESIGN DECISION:",
|
|
24
|
-
"## Architecture",
|
|
25
|
-
|
|
26
|
-
# Important constraints
|
|
27
29
|
"CONSTRAINT:",
|
|
28
30
|
"REQUIREMENT:",
|
|
29
31
|
"MUST NOT:",
|
|
30
32
|
"NEVER:",
|
|
31
|
-
|
|
32
|
-
# Session state
|
|
33
|
+
"CRITICAL ERROR:",
|
|
33
34
|
"CURRENT TASK:",
|
|
34
35
|
"BLOCKED BY:",
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
# Critical errors
|
|
38
|
-
"CRITICAL ERROR:",
|
|
39
|
-
"SECURITY ISSUE:",
|
|
40
|
-
"BREAKING CHANGE:",
|
|
36
|
+
"[STRAVINSKY MODE]",
|
|
37
|
+
"PARALLEL_DELEGATION:",
|
|
41
38
|
]
|
|
42
39
|
|
|
43
|
-
# Memory anchors to inject into compaction
|
|
44
|
-
MEMORY_ANCHORS: List[str] = []
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def register_memory_anchor(anchor: str, priority: str = "normal"):
|
|
48
|
-
"""
|
|
49
|
-
Register a memory anchor to preserve during compaction.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
anchor: The text to preserve
|
|
53
|
-
priority: "critical" or "normal"
|
|
54
|
-
"""
|
|
55
|
-
if priority == "critical":
|
|
56
|
-
MEMORY_ANCHORS.insert(0, f"[CRITICAL] {anchor}")
|
|
57
|
-
else:
|
|
58
|
-
MEMORY_ANCHORS.append(anchor)
|
|
59
|
-
|
|
60
|
-
# Limit to 10 anchors to prevent bloat
|
|
61
|
-
while len(MEMORY_ANCHORS) > 10:
|
|
62
|
-
MEMORY_ANCHORS.pop()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def clear_memory_anchors():
|
|
66
|
-
"""Clear all registered memory anchors."""
|
|
67
|
-
MEMORY_ANCHORS.clear()
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
async def pre_compact_hook(params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
71
|
-
"""
|
|
72
|
-
Pre-model-invoke hook that runs before context compaction.
|
|
73
|
-
|
|
74
|
-
Uses Gemini to intelligently extract and preserve critical context
|
|
75
|
-
that should survive summarization.
|
|
76
|
-
"""
|
|
77
|
-
global _in_preservation
|
|
78
|
-
|
|
79
|
-
# Prevent recursive calls
|
|
80
|
-
if _in_preservation:
|
|
81
|
-
return None
|
|
82
|
-
|
|
83
|
-
prompt = params.get("prompt", "")
|
|
84
|
-
|
|
85
|
-
# Only activate for compaction-related prompts
|
|
86
|
-
if not _is_compaction_prompt(prompt):
|
|
87
|
-
return None
|
|
88
|
-
|
|
89
|
-
# Collect pattern-matched context
|
|
90
|
-
preserved_context = _extract_preserved_context(prompt)
|
|
91
|
-
preserved_context.extend(MEMORY_ANCHORS)
|
|
92
|
-
|
|
93
|
-
# Use Gemini for intelligent context extraction if prompt is long
|
|
94
|
-
if len(prompt) > 50000:
|
|
95
|
-
try:
|
|
96
|
-
_in_preservation = True
|
|
97
|
-
gemini_context = await _extract_context_with_gemini(prompt)
|
|
98
|
-
if gemini_context:
|
|
99
|
-
preserved_context.extend(gemini_context)
|
|
100
|
-
except Exception as e:
|
|
101
|
-
logger.warning(f"[PreCompactHook] Gemini extraction failed: {e}")
|
|
102
|
-
finally:
|
|
103
|
-
_in_preservation = False
|
|
104
|
-
|
|
105
|
-
if not preserved_context:
|
|
106
|
-
return None
|
|
107
|
-
|
|
108
|
-
# Build preservation section
|
|
109
|
-
preservation_section = _build_preservation_section(preserved_context)
|
|
110
|
-
|
|
111
|
-
logger.info(f"[PreCompactHook] Preserving {len(preserved_context)} context items")
|
|
112
|
-
|
|
113
|
-
# Inject into prompt
|
|
114
|
-
modified_prompt = prompt + "\n\n" + preservation_section
|
|
115
|
-
|
|
116
|
-
return {**params, "prompt": modified_prompt}
|
|
117
40
|
|
|
41
|
+
def ensure_state_dir():
|
|
42
|
+
"""Ensure state directory exists."""
|
|
43
|
+
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
118
44
|
|
|
119
|
-
async def _extract_context_with_gemini(prompt: str) -> List[str]:
|
|
120
|
-
"""
|
|
121
|
-
Use Gemini to intelligently extract critical context to preserve.
|
|
122
45
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
List of critical context items to preserve
|
|
128
|
-
"""
|
|
46
|
+
def get_stravinsky_mode_state() -> Dict[str, Any]:
|
|
47
|
+
"""Read stravinsky mode state."""
|
|
48
|
+
if not STRAVINSKY_MODE_FILE.exists():
|
|
49
|
+
return {"active": False}
|
|
129
50
|
try:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
truncated = prompt[:max_chars] if len(prompt) > max_chars else prompt
|
|
135
|
-
|
|
136
|
-
extraction_prompt = f"""Analyze this conversation and extract ONLY the most critical information that MUST be preserved during summarization.
|
|
137
|
-
|
|
138
|
-
Focus on:
|
|
139
|
-
1. Architecture decisions and their rationale
|
|
140
|
-
2. Critical constraints or requirements
|
|
141
|
-
3. Important error patterns or debugging insights
|
|
142
|
-
4. Key file paths and their purposes
|
|
143
|
-
5. Unfinished tasks or blocking issues
|
|
144
|
-
|
|
145
|
-
Return a bullet list of critical items (max 10). Be extremely concise.
|
|
146
|
-
|
|
147
|
-
CONVERSATION:
|
|
148
|
-
{truncated}
|
|
51
|
+
content = STRAVINSKY_MODE_FILE.read_text().strip()
|
|
52
|
+
return json.loads(content) if content else {"active": True}
|
|
53
|
+
except (json.JSONDecodeError, IOError):
|
|
54
|
+
return {"active": True}
|
|
149
55
|
|
|
150
|
-
CRITICAL ITEMS TO PRESERVE:"""
|
|
151
56
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
temperature=0.1,
|
|
157
|
-
)
|
|
57
|
+
def extract_preserved_context(prompt: str) -> List[str]:
|
|
58
|
+
"""Extract context matching preservation patterns."""
|
|
59
|
+
preserved = []
|
|
60
|
+
lines = prompt.split("\n")
|
|
158
61
|
|
|
159
|
-
|
|
160
|
-
|
|
62
|
+
for i, line in enumerate(lines):
|
|
63
|
+
for pattern in PRESERVE_PATTERNS:
|
|
64
|
+
if pattern in line:
|
|
65
|
+
# Capture line + 2 more for context
|
|
66
|
+
context = "\n".join(lines[i:min(i+3, len(lines))])
|
|
67
|
+
preserved.append(context)
|
|
68
|
+
break
|
|
161
69
|
|
|
162
|
-
|
|
163
|
-
lines = result.strip().split("\n")
|
|
164
|
-
items = []
|
|
165
|
-
for line in lines:
|
|
166
|
-
line = line.strip()
|
|
167
|
-
if line.startswith(("-", "*", "•")) or (len(line) > 1 and line[0].isdigit() and line[1] in ".):"):
|
|
168
|
-
# Clean up the bullet
|
|
169
|
-
item = line.lstrip("-*•0123456789.): ").strip()
|
|
170
|
-
if item and len(item) > 10:
|
|
171
|
-
items.append(item)
|
|
70
|
+
return preserved[:15] # Max 15 items
|
|
172
71
|
|
|
173
|
-
return items[:10] # Max 10 items
|
|
174
72
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
73
|
+
def log_compaction(preserved: List[str], stravinsky_active: bool):
|
|
74
|
+
"""Log compaction event for audit."""
|
|
75
|
+
ensure_state_dir()
|
|
178
76
|
|
|
77
|
+
entry = {
|
|
78
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
79
|
+
"preserved_count": len(preserved),
|
|
80
|
+
"stravinsky_mode": stravinsky_active,
|
|
81
|
+
"preview": [p[:50] for p in preserved[:3]],
|
|
82
|
+
}
|
|
179
83
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"reduce context size",
|
|
186
|
-
"context window",
|
|
187
|
-
"summarization",
|
|
188
|
-
]
|
|
84
|
+
try:
|
|
85
|
+
with COMPACTION_LOG.open("a") as f:
|
|
86
|
+
f.write(json.dumps(entry) + "\n")
|
|
87
|
+
except IOError:
|
|
88
|
+
pass
|
|
189
89
|
|
|
190
|
-
prompt_lower = prompt.lower()
|
|
191
|
-
return any(signal in prompt_lower for signal in compaction_signals)
|
|
192
90
|
|
|
91
|
+
def main():
|
|
92
|
+
"""Main hook entry point."""
|
|
93
|
+
try:
|
|
94
|
+
hook_input = json.load(sys.stdin)
|
|
95
|
+
except (json.JSONDecodeError, EOFError):
|
|
96
|
+
return 0
|
|
193
97
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
preserved = []
|
|
197
|
-
lines = prompt.split("\n")
|
|
98
|
+
prompt = hook_input.get("prompt", "")
|
|
99
|
+
trigger = hook_input.get("trigger", "auto")
|
|
198
100
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
# Capture the line and next 2 lines for context
|
|
203
|
-
context_lines = lines[i:i+3]
|
|
204
|
-
preserved.append("\n".join(context_lines))
|
|
205
|
-
break
|
|
101
|
+
# Get stravinsky mode state
|
|
102
|
+
strav_state = get_stravinsky_mode_state()
|
|
103
|
+
stravinsky_active = strav_state.get("active", False)
|
|
206
104
|
|
|
207
|
-
|
|
105
|
+
# Extract preserved context
|
|
106
|
+
preserved = extract_preserved_context(prompt)
|
|
208
107
|
|
|
108
|
+
# Log compaction event
|
|
109
|
+
log_compaction(preserved, stravinsky_active)
|
|
209
110
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
111
|
+
# Output preservation warning
|
|
112
|
+
if preserved or stravinsky_active:
|
|
113
|
+
print(f"\n[PreCompact] Context compaction triggered ({trigger})", file=sys.stderr)
|
|
114
|
+
print(f" Preserved items: {len(preserved)}", file=sys.stderr)
|
|
115
|
+
if stravinsky_active:
|
|
116
|
+
print(" [STRAVINSKY MODE ACTIVE] - State will persist", file=sys.stderr)
|
|
117
|
+
print(" Audit log: ~/.claude/state/compaction.jsonl", file=sys.stderr)
|
|
214
118
|
|
|
215
|
-
|
|
119
|
+
return 0
|
|
216
120
|
|
|
217
|
-
"""
|
|
218
|
-
for i, item in enumerate(context_items, 1):
|
|
219
|
-
section += f"{i}. {item}\n\n"
|
|
220
121
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
"""
|
|
224
|
-
return section
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
sys.exit(main())
|