stravinsky 0.4.18__py3-none-any.whl → 0.4.66__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.

Files changed (184) hide show
  1. mcp_bridge/__init__.py +1 -1
  2. mcp_bridge/auth/__init__.py +16 -6
  3. mcp_bridge/auth/cli.py +202 -11
  4. mcp_bridge/auth/oauth.py +1 -2
  5. mcp_bridge/auth/openai_oauth.py +4 -7
  6. mcp_bridge/auth/token_store.py +0 -1
  7. mcp_bridge/cli/__init__.py +1 -1
  8. mcp_bridge/cli/install_hooks.py +503 -107
  9. mcp_bridge/cli/session_report.py +0 -3
  10. mcp_bridge/config/__init__.py +2 -2
  11. mcp_bridge/config/hook_config.py +3 -5
  12. mcp_bridge/config/rate_limits.py +108 -13
  13. mcp_bridge/hooks/HOOKS_SETTINGS.json +17 -4
  14. mcp_bridge/hooks/__init__.py +14 -4
  15. mcp_bridge/hooks/agent_reminder.py +4 -4
  16. mcp_bridge/hooks/auto_slash_command.py +5 -5
  17. mcp_bridge/hooks/budget_optimizer.py +2 -2
  18. mcp_bridge/hooks/claude_limits_hook.py +114 -0
  19. mcp_bridge/hooks/comment_checker.py +3 -4
  20. mcp_bridge/hooks/compaction.py +2 -2
  21. mcp_bridge/hooks/context.py +2 -1
  22. mcp_bridge/hooks/context_monitor.py +2 -2
  23. mcp_bridge/hooks/delegation_policy.py +85 -0
  24. mcp_bridge/hooks/directory_context.py +3 -3
  25. mcp_bridge/hooks/edit_recovery.py +3 -2
  26. mcp_bridge/hooks/edit_recovery_policy.py +49 -0
  27. mcp_bridge/hooks/empty_message_sanitizer.py +2 -2
  28. mcp_bridge/hooks/events.py +160 -0
  29. mcp_bridge/hooks/git_noninteractive.py +4 -4
  30. mcp_bridge/hooks/keyword_detector.py +8 -10
  31. mcp_bridge/hooks/manager.py +35 -22
  32. mcp_bridge/hooks/notification_hook.py +13 -6
  33. mcp_bridge/hooks/parallel_enforcement_policy.py +67 -0
  34. mcp_bridge/hooks/parallel_enforcer.py +5 -5
  35. mcp_bridge/hooks/parallel_execution.py +22 -10
  36. mcp_bridge/hooks/post_tool/parallel_validation.py +103 -0
  37. mcp_bridge/hooks/pre_compact.py +8 -9
  38. mcp_bridge/hooks/pre_tool/agent_spawn_validator.py +115 -0
  39. mcp_bridge/hooks/preemptive_compaction.py +2 -3
  40. mcp_bridge/hooks/routing_notifications.py +80 -0
  41. mcp_bridge/hooks/rules_injector.py +11 -19
  42. mcp_bridge/hooks/session_idle.py +4 -4
  43. mcp_bridge/hooks/session_notifier.py +4 -4
  44. mcp_bridge/hooks/session_recovery.py +4 -5
  45. mcp_bridge/hooks/stravinsky_mode.py +1 -1
  46. mcp_bridge/hooks/subagent_stop.py +1 -3
  47. mcp_bridge/hooks/task_validator.py +2 -2
  48. mcp_bridge/hooks/tmux_manager.py +7 -8
  49. mcp_bridge/hooks/todo_delegation.py +4 -1
  50. mcp_bridge/hooks/todo_enforcer.py +180 -10
  51. mcp_bridge/hooks/truncation_policy.py +37 -0
  52. mcp_bridge/hooks/truncator.py +1 -2
  53. mcp_bridge/metrics/cost_tracker.py +115 -0
  54. mcp_bridge/native_search.py +93 -0
  55. mcp_bridge/native_watcher.py +118 -0
  56. mcp_bridge/notifications.py +3 -4
  57. mcp_bridge/orchestrator/enums.py +11 -0
  58. mcp_bridge/orchestrator/router.py +165 -0
  59. mcp_bridge/orchestrator/state.py +32 -0
  60. mcp_bridge/orchestrator/visualization.py +14 -0
  61. mcp_bridge/orchestrator/wisdom.py +34 -0
  62. mcp_bridge/prompts/__init__.py +1 -8
  63. mcp_bridge/prompts/dewey.py +1 -1
  64. mcp_bridge/prompts/planner.py +2 -4
  65. mcp_bridge/prompts/stravinsky.py +53 -31
  66. mcp_bridge/proxy/__init__.py +0 -0
  67. mcp_bridge/proxy/client.py +70 -0
  68. mcp_bridge/proxy/model_server.py +157 -0
  69. mcp_bridge/routing/__init__.py +43 -0
  70. mcp_bridge/routing/config.py +250 -0
  71. mcp_bridge/routing/model_tiers.py +135 -0
  72. mcp_bridge/routing/provider_state.py +261 -0
  73. mcp_bridge/routing/task_classifier.py +190 -0
  74. mcp_bridge/server.py +363 -34
  75. mcp_bridge/server_tools.py +298 -6
  76. mcp_bridge/tools/__init__.py +19 -8
  77. mcp_bridge/tools/agent_manager.py +549 -799
  78. mcp_bridge/tools/background_tasks.py +13 -17
  79. mcp_bridge/tools/code_search.py +54 -51
  80. mcp_bridge/tools/continuous_loop.py +0 -1
  81. mcp_bridge/tools/dashboard.py +19 -0
  82. mcp_bridge/tools/find_code.py +296 -0
  83. mcp_bridge/tools/init.py +1 -0
  84. mcp_bridge/tools/list_directory.py +42 -0
  85. mcp_bridge/tools/lsp/__init__.py +8 -8
  86. mcp_bridge/tools/lsp/manager.py +51 -28
  87. mcp_bridge/tools/lsp/tools.py +98 -65
  88. mcp_bridge/tools/model_invoke.py +1047 -152
  89. mcp_bridge/tools/mux_client.py +75 -0
  90. mcp_bridge/tools/project_context.py +1 -2
  91. mcp_bridge/tools/query_classifier.py +132 -49
  92. mcp_bridge/tools/read_file.py +84 -0
  93. mcp_bridge/tools/replace.py +45 -0
  94. mcp_bridge/tools/run_shell_command.py +38 -0
  95. mcp_bridge/tools/search_enhancements.py +347 -0
  96. mcp_bridge/tools/semantic_search.py +677 -92
  97. mcp_bridge/tools/session_manager.py +0 -2
  98. mcp_bridge/tools/skill_loader.py +0 -1
  99. mcp_bridge/tools/task_runner.py +5 -7
  100. mcp_bridge/tools/templates.py +3 -3
  101. mcp_bridge/tools/tool_search.py +331 -0
  102. mcp_bridge/tools/write_file.py +29 -0
  103. mcp_bridge/update_manager.py +33 -37
  104. mcp_bridge/update_manager_pypi.py +6 -8
  105. mcp_bridge/utils/cache.py +82 -0
  106. mcp_bridge/utils/process.py +71 -0
  107. mcp_bridge/utils/session_state.py +51 -0
  108. mcp_bridge/utils/truncation.py +76 -0
  109. {stravinsky-0.4.18.dist-info → stravinsky-0.4.66.dist-info}/METADATA +84 -35
  110. stravinsky-0.4.66.dist-info/RECORD +198 -0
  111. {stravinsky-0.4.18.dist-info → stravinsky-0.4.66.dist-info}/entry_points.txt +1 -0
  112. stravinsky_claude_assets/HOOKS_INTEGRATION.md +316 -0
  113. stravinsky_claude_assets/agents/HOOKS.md +437 -0
  114. stravinsky_claude_assets/agents/code-reviewer.md +210 -0
  115. stravinsky_claude_assets/agents/comment_checker.md +580 -0
  116. stravinsky_claude_assets/agents/debugger.md +254 -0
  117. stravinsky_claude_assets/agents/delphi.md +495 -0
  118. stravinsky_claude_assets/agents/dewey.md +248 -0
  119. stravinsky_claude_assets/agents/explore.md +1198 -0
  120. stravinsky_claude_assets/agents/frontend.md +472 -0
  121. stravinsky_claude_assets/agents/implementation-lead.md +164 -0
  122. stravinsky_claude_assets/agents/momus.md +464 -0
  123. stravinsky_claude_assets/agents/research-lead.md +141 -0
  124. stravinsky_claude_assets/agents/stravinsky.md +730 -0
  125. stravinsky_claude_assets/commands/delphi.md +9 -0
  126. stravinsky_claude_assets/commands/dewey.md +54 -0
  127. stravinsky_claude_assets/commands/git-master.md +112 -0
  128. stravinsky_claude_assets/commands/index.md +49 -0
  129. stravinsky_claude_assets/commands/publish.md +86 -0
  130. stravinsky_claude_assets/commands/review.md +73 -0
  131. stravinsky_claude_assets/commands/str/agent_cancel.md +70 -0
  132. stravinsky_claude_assets/commands/str/agent_list.md +56 -0
  133. stravinsky_claude_assets/commands/str/agent_output.md +92 -0
  134. stravinsky_claude_assets/commands/str/agent_progress.md +74 -0
  135. stravinsky_claude_assets/commands/str/agent_retry.md +94 -0
  136. stravinsky_claude_assets/commands/str/cancel.md +51 -0
  137. stravinsky_claude_assets/commands/str/clean.md +97 -0
  138. stravinsky_claude_assets/commands/str/continue.md +38 -0
  139. stravinsky_claude_assets/commands/str/index.md +199 -0
  140. stravinsky_claude_assets/commands/str/list_watchers.md +96 -0
  141. stravinsky_claude_assets/commands/str/search.md +205 -0
  142. stravinsky_claude_assets/commands/str/start_filewatch.md +136 -0
  143. stravinsky_claude_assets/commands/str/stats.md +71 -0
  144. stravinsky_claude_assets/commands/str/stop_filewatch.md +89 -0
  145. stravinsky_claude_assets/commands/str/unwatch.md +42 -0
  146. stravinsky_claude_assets/commands/str/watch.md +45 -0
  147. stravinsky_claude_assets/commands/strav.md +53 -0
  148. stravinsky_claude_assets/commands/stravinsky.md +292 -0
  149. stravinsky_claude_assets/commands/verify.md +60 -0
  150. stravinsky_claude_assets/commands/version.md +5 -0
  151. stravinsky_claude_assets/hooks/README.md +248 -0
  152. stravinsky_claude_assets/hooks/comment_checker.py +193 -0
  153. stravinsky_claude_assets/hooks/context.py +38 -0
  154. stravinsky_claude_assets/hooks/context_monitor.py +153 -0
  155. stravinsky_claude_assets/hooks/dependency_tracker.py +73 -0
  156. stravinsky_claude_assets/hooks/edit_recovery.py +46 -0
  157. stravinsky_claude_assets/hooks/execution_state_tracker.py +68 -0
  158. stravinsky_claude_assets/hooks/notification_hook.py +103 -0
  159. stravinsky_claude_assets/hooks/notification_hook_v2.py +96 -0
  160. stravinsky_claude_assets/hooks/parallel_execution.py +241 -0
  161. stravinsky_claude_assets/hooks/parallel_reinforcement.py +106 -0
  162. stravinsky_claude_assets/hooks/parallel_reinforcement_v2.py +112 -0
  163. stravinsky_claude_assets/hooks/pre_compact.py +123 -0
  164. stravinsky_claude_assets/hooks/ralph_loop.py +173 -0
  165. stravinsky_claude_assets/hooks/session_recovery.py +263 -0
  166. stravinsky_claude_assets/hooks/stop_hook.py +89 -0
  167. stravinsky_claude_assets/hooks/stravinsky_metrics.py +164 -0
  168. stravinsky_claude_assets/hooks/stravinsky_mode.py +146 -0
  169. stravinsky_claude_assets/hooks/subagent_stop.py +98 -0
  170. stravinsky_claude_assets/hooks/todo_continuation.py +111 -0
  171. stravinsky_claude_assets/hooks/todo_delegation.py +96 -0
  172. stravinsky_claude_assets/hooks/tool_messaging.py +281 -0
  173. stravinsky_claude_assets/hooks/truncator.py +23 -0
  174. stravinsky_claude_assets/rules/deployment_safety.md +51 -0
  175. stravinsky_claude_assets/rules/integration_wiring.md +89 -0
  176. stravinsky_claude_assets/rules/pypi_deployment.md +220 -0
  177. stravinsky_claude_assets/rules/stravinsky_orchestrator.md +32 -0
  178. stravinsky_claude_assets/settings.json +152 -0
  179. stravinsky_claude_assets/skills/chrome-devtools/SKILL.md +81 -0
  180. stravinsky_claude_assets/skills/sqlite/SKILL.md +77 -0
  181. stravinsky_claude_assets/skills/supabase/SKILL.md +74 -0
  182. stravinsky_claude_assets/task_dependencies.json +34 -0
  183. stravinsky-0.4.18.dist-info/RECORD +0 -88
  184. {stravinsky-0.4.18.dist-info → stravinsky-0.4.66.dist-info}/WHEEL +0 -0
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook for user-friendly tool messaging.
4
+
5
+ Outputs concise messages about which agent/tool was used and what it did.
6
+ Format examples:
7
+ - ast-grep('Searching for authentication patterns')
8
+ - delphi:openai/gpt-5.2-medium('Analyzing architecture trade-offs')
9
+ - explore:gemini-3-flash('Finding all API endpoints')
10
+ """
11
+
12
+ import json
13
+ import os
14
+ import sys
15
+
16
+ # Add utils directory to path for imports
17
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "utils"))
18
+ from colors import get_agent_color, colorize, Color, supports_color
19
+ from console_format import format_tool_use, format_agent_spawn, MessageType
20
+
21
+ # Agent model mappings
22
+ AGENT_MODELS = {
23
+ "explore": "gemini-3-flash",
24
+ "dewey": "gemini-3-flash",
25
+ "code-reviewer": "sonnet",
26
+ "debugger": "sonnet",
27
+ "frontend": "gemini-3-pro-high",
28
+ "delphi": "gpt-5.2-medium",
29
+ }
30
+
31
+ # MCP Server emoji mappings
32
+ SERVER_EMOJIS = {
33
+ "github": "🟡",
34
+ "ast-grep": "🟤",
35
+ "grep-app": "🟣",
36
+ "MCP_DOCKER": "🔵",
37
+ "stravinsky": "🔧",
38
+ }
39
+
40
+ # Tool display names (legacy mapping for simple tools)
41
+ TOOL_NAMES = {
42
+ "mcp__stravinsky__ast_grep_search": "ast-grep",
43
+ "mcp__stravinsky__grep_search": "grep",
44
+ "mcp__stravinsky__glob_files": "glob",
45
+ "mcp__stravinsky__lsp_diagnostics": "lsp-diagnostics",
46
+ "mcp__stravinsky__lsp_hover": "lsp-hover",
47
+ "mcp__stravinsky__lsp_goto_definition": "lsp-goto-def",
48
+ "mcp__stravinsky__lsp_find_references": "lsp-find-refs",
49
+ "mcp__stravinsky__lsp_document_symbols": "lsp-symbols",
50
+ "mcp__stravinsky__lsp_workspace_symbols": "lsp-workspace-symbols",
51
+ "mcp__stravinsky__invoke_gemini": "gemini",
52
+ "mcp__stravinsky__invoke_openai": "openai",
53
+ "mcp__grep-app__searchCode": "grep.app",
54
+ "mcp__grep-app__github_file": "github-file",
55
+ }
56
+
57
+
58
+ def parse_mcp_tool_name(tool_name: str) -> tuple[str, str, str]:
59
+ """
60
+ Parse MCP tool name into (server, tool_type, emoji).
61
+
62
+ Examples:
63
+ mcp__github__get_file_contents -> ("github", "get_file_contents", "🟡")
64
+ mcp__stravinsky__grep_search -> ("stravinsky", "grep", "🔧")
65
+ mcp__ast-grep__find_code -> ("ast-grep", "find_code", "🟤")
66
+ """
67
+ if not tool_name.startswith("mcp__"):
68
+ return ("unknown", tool_name, "🔧")
69
+
70
+ # Remove mcp__ prefix and split by __
71
+ parts = tool_name[5:].split("__", 1)
72
+ if len(parts) != 2:
73
+ return ("unknown", tool_name, "🔧")
74
+
75
+ server = parts[0]
76
+ tool_type = parts[1]
77
+
78
+ # Get emoji for server
79
+ emoji = SERVER_EMOJIS.get(server, "🔧")
80
+
81
+ # Get simplified tool name if available
82
+ simple_name = TOOL_NAMES.get(tool_name, tool_type)
83
+
84
+ return (server, simple_name, emoji)
85
+
86
+
87
+ def extract_description(tool_name: str, params: dict) -> str:
88
+ """Extract a concise description of what the tool did."""
89
+
90
+ # GitHub tools
91
+ if "github" in tool_name.lower():
92
+ if "get_file_contents" in tool_name:
93
+ path = params.get("path", "")
94
+ repo = params.get("repo", "")
95
+ owner = params.get("owner", "")
96
+ return f"Fetching {path} from {owner}/{repo}"
97
+ elif "create_or_update_file" in tool_name:
98
+ path = params.get("path", "")
99
+ return f"Updating {path}"
100
+ elif "search_repositories" in tool_name:
101
+ query = params.get("query", "")
102
+ return f"Searching repos for '{query[:40]}'"
103
+ elif "search_code" in tool_name:
104
+ q = params.get("q", "")
105
+ return f"Searching code for '{q[:40]}'"
106
+ elif "create_pull_request" in tool_name:
107
+ title = params.get("title", "")
108
+ return f"Creating PR: {title[:40]}"
109
+ elif "get_pull_request" in tool_name or "list_pull_requests" in tool_name:
110
+ return "Fetching PR details"
111
+ return "GitHub operation"
112
+
113
+ # MCP_DOCKER tools
114
+ if "MCP_DOCKER" in tool_name:
115
+ if "web_search_exa" in tool_name:
116
+ query = params.get("query", "")
117
+ return f"Web search: '{query[:40]}'"
118
+ elif "create_entities" in tool_name:
119
+ entities = params.get("entities", [])
120
+ count = len(entities)
121
+ return f"Creating {count} knowledge graph entities"
122
+ elif "search_nodes" in tool_name:
123
+ query = params.get("query", "")
124
+ return f"Searching knowledge graph for '{query[:40]}'"
125
+ return "Knowledge graph operation"
126
+
127
+ # ast-grep tools
128
+ if "ast-grep" in tool_name or "ast_grep" in tool_name:
129
+ if "find_code" in tool_name or "search" in tool_name:
130
+ pattern = params.get("pattern", "")
131
+ return f"AST search for '{pattern[:40]}'"
132
+ elif "test_match" in tool_name:
133
+ return "Testing AST pattern"
134
+ elif "dump_syntax" in tool_name:
135
+ return "Dumping syntax tree"
136
+ return "AST operation"
137
+
138
+ # grep-app tools
139
+ if "grep-app" in tool_name or "grep_app" in tool_name:
140
+ if "searchCode" in tool_name:
141
+ query = params.get("query", "")
142
+ return f"Searching GitHub for '{query[:40]}'"
143
+ elif "github_file" in tool_name:
144
+ path = params.get("path", "")
145
+ repo = params.get("repo", "")
146
+ return f"Fetching {path} from {repo}"
147
+ return "grep.app search"
148
+
149
+ # AST-grep (stravinsky)
150
+ if "ast_grep" in tool_name:
151
+ pattern = params.get("pattern", "")
152
+ directory = params.get("directory", ".")
153
+ return f"Searching AST in {directory} for '{pattern[:40]}...'"
154
+
155
+ # Grep/search
156
+ if "grep_search" in tool_name or "searchCode" in tool_name:
157
+ pattern = params.get("pattern", params.get("query", ""))
158
+ return f"Searching for '{pattern[:40]}...'"
159
+
160
+ # Glob
161
+ if "glob_files" in tool_name:
162
+ pattern = params.get("pattern", "")
163
+ return f"Finding files matching '{pattern}'"
164
+
165
+ # LSP diagnostics
166
+ if "lsp_diagnostics" in tool_name:
167
+ file_path = params.get("file_path", "")
168
+ filename = os.path.basename(file_path) if file_path else "file"
169
+ return f"Checking {filename} for errors"
170
+
171
+ # LSP hover
172
+ if "lsp_hover" in tool_name:
173
+ file_path = params.get("file_path", "")
174
+ line = params.get("line", "")
175
+ filename = os.path.basename(file_path) if file_path else "file"
176
+ return f"Type info for {filename}:{line}"
177
+
178
+ # LSP goto definition
179
+ if "lsp_goto" in tool_name:
180
+ file_path = params.get("file_path", "")
181
+ filename = os.path.basename(file_path) if file_path else "symbol"
182
+ return f"Finding definition in {filename}"
183
+
184
+ # LSP find references
185
+ if "lsp_find_references" in tool_name:
186
+ file_path = params.get("file_path", "")
187
+ filename = os.path.basename(file_path) if file_path else "symbol"
188
+ return f"Finding all references to symbol in {filename}"
189
+
190
+ # LSP symbols
191
+ if "lsp_symbols" in tool_name or "lsp_document_symbols" in tool_name:
192
+ file_path = params.get("file_path", "")
193
+ filename = os.path.basename(file_path) if file_path else "file"
194
+ return f"Getting symbols from {filename}"
195
+
196
+ if "lsp_workspace_symbols" in tool_name:
197
+ query = params.get("query", "")
198
+ return f"Searching workspace for symbol '{query}'"
199
+
200
+ # Gemini invocation
201
+ if "invoke_gemini" in tool_name:
202
+ prompt = params.get("prompt", "")
203
+ # Extract first meaningful line
204
+ first_line = prompt.split("\n")[0][:50] if prompt else "Processing"
205
+ return first_line
206
+
207
+ # OpenAI invocation
208
+ if "invoke_openai" in tool_name:
209
+ prompt = params.get("prompt", "")
210
+ first_line = prompt.split("\n")[0][:50] if prompt else "Strategic analysis"
211
+ return first_line
212
+
213
+ # GitHub file fetch
214
+ if "github_file" in tool_name:
215
+ path = params.get("path", "")
216
+ repo = params.get("repo", "")
217
+ return f"Fetching {path} from {repo}"
218
+
219
+ # Agent spawn (MCP tool)
220
+ if "agent_spawn" in tool_name:
221
+ agent_type = params.get("agent_type", "unknown")
222
+ description = params.get("description", "")
223
+ model = AGENT_MODELS.get(agent_type, "gemini-3-flash")
224
+ return f"{agent_type}({model})"
225
+
226
+ # Task delegation
227
+ if tool_name == "Task":
228
+ subagent_type = params.get("subagent_type", "unknown")
229
+ description = params.get("description", "")
230
+ model = AGENT_MODELS.get(subagent_type, "unknown")
231
+ return f"{subagent_type}:{model}('{description}')"
232
+
233
+ return "Processing"
234
+
235
+
236
+ def main():
237
+ try:
238
+ # Read hook input from stdin
239
+ hook_input = json.loads(sys.stdin.read())
240
+
241
+ tool_name = hook_input.get("toolName", hook_input.get("tool_name", ""))
242
+ params = hook_input.get("params", hook_input.get("tool_input", {}))
243
+
244
+ # Only output messages for MCP tools and Task delegations
245
+ if not (tool_name.startswith("mcp__") or tool_name == "Task"):
246
+ sys.exit(0)
247
+
248
+ # Special handling for Task delegations
249
+ if tool_name == "Task":
250
+ subagent_type = params.get("subagent_type", "unknown")
251
+ description = params.get("description", "")
252
+ model = AGENT_MODELS.get(subagent_type, "unknown")
253
+
254
+ # Use rich formatting for agent spawns
255
+ message = format_agent_spawn(
256
+ agent_type=subagent_type, model=model, description=description
257
+ )
258
+ print(message, file=sys.stderr)
259
+ else:
260
+ # Parse MCP tool name to get server, tool_type, and emoji
261
+ server, tool_type, emoji = parse_mcp_tool_name(tool_name)
262
+
263
+ # Get description of what the tool did
264
+ description = extract_description(tool_name, params)
265
+
266
+ # Use rich formatting for tool usage
267
+ message = format_tool_use(
268
+ tool_name=tool_type, server=server, description=description, emoji=emoji
269
+ )
270
+ print(message, file=sys.stderr)
271
+
272
+ sys.exit(0)
273
+
274
+ except Exception as e:
275
+ # On error, fail silently (don't disrupt workflow)
276
+ print(f"Tool messaging hook error: {e}", file=sys.stderr)
277
+ sys.exit(0)
278
+
279
+
280
+ if __name__ == "__main__":
281
+ main()
@@ -0,0 +1,23 @@
1
+ import os
2
+ import sys
3
+ import json
4
+
5
+ MAX_CHARS = 30000
6
+
7
+ def main():
8
+ try:
9
+ data = json.load(sys.stdin)
10
+ tool_response = data.get("tool_response", "")
11
+ except Exception:
12
+ return
13
+
14
+ if len(tool_response) > MAX_CHARS:
15
+ header = f"[TRUNCATED - {len(tool_response)} chars reduced to {MAX_CHARS}]\n"
16
+ footer = "\n...[TRUNCATED]"
17
+ truncated = tool_response[:MAX_CHARS]
18
+ print(header + truncated + footer)
19
+ else:
20
+ print(tool_response)
21
+
22
+ if __name__ == "__main__":
23
+ main()
@@ -0,0 +1,51 @@
1
+ # Deployment Safety Rules
2
+
3
+ ## CRITICAL: Pre-Deployment Checklist
4
+
5
+ **NEVER deploy code to PyPI without passing ALL checks:**
6
+
7
+ ```bash
8
+ # Run this BEFORE every deployment
9
+ ./pre_deploy_check.sh
10
+ ```
11
+
12
+ ## Mandatory Checks (BLOCKING)
13
+
14
+ 1. **Import Test** - `python3 -c "import mcp_bridge.server"` must succeed
15
+ 2. **Version Consistency** - pyproject.toml and __init__.py versions must match
16
+ 3. **Command Works** - `stravinsky --version` must succeed
17
+ 4. **All Tools Import** - Every tool module must import without errors
18
+ 5. **Tests Pass** - If tests exist, `pytest` must pass
19
+ 6. **Linting Clean** - `ruff check` must have zero errors
20
+ 7. **Git Clean** - No uncommitted changes
21
+
22
+ ## Deployment Process
23
+
24
+ ```bash
25
+ # Step 1: Run safety checks
26
+ ./pre_deploy_check.sh || {
27
+ echo "❌ FAILED: Fix errors before deploying"
28
+ exit 1
29
+ }
30
+
31
+ # Step 2: Deploy (only if checks pass)
32
+ ./deploy.sh
33
+ ```
34
+
35
+ ## Why This Matters
36
+
37
+ **Recent failures prevented by these checks:**
38
+ - **v0.4.30 (2026-01-09)**: `NameError: name 'logger' is not defined` - would be caught by import test
39
+ - Future: Type errors, missing imports, broken commands all caught before PyPI
40
+
41
+ ## Consequences of Skipping Checks
42
+
43
+ - Broken installations for all users globally
44
+ - Version number burned (can't re-upload to PyPI)
45
+ - Force version bump to fix
46
+ - User trust eroded
47
+ - Support burden increased
48
+
49
+ ## Rule
50
+
51
+ **You MUST run `./pre_deploy_check.sh` before EVERY deployment. NO EXCEPTIONS.**
@@ -0,0 +1,89 @@
1
+ # Integration Wiring Rules
2
+
3
+ ## Lesson Learned: Tool-Agent Integration (2026-01-11)
4
+
5
+ **Problem**: Sub-agents fail silently when tools are "wired in" but not actually usable.
6
+
7
+ **Root Cause**: Adding a tool to an agent's tool list doesn't guarantee the agent can USE it.
8
+
9
+ ## Integration Checklist (MANDATORY)
10
+
11
+ When integrating Tool X into Agent Y:
12
+
13
+ ### 1. Verify Invocation Method
14
+
15
+ | Agent Type | Invocation Method | Tool Access |
16
+ |------------|-------------------|-------------|
17
+ | `invoke_gemini` | Simple completion | NO tool calling |
18
+ | `invoke_gemini_agentic` | Agentic loop | YES - full tool access |
19
+ | `invoke_openai` | Direct call | Depends on model |
20
+
21
+ **CRITICAL**: If agent needs to call tools, it MUST use the `_agentic` variant.
22
+
23
+ ```python
24
+ # WRONG: Agent can't use tools
25
+ invoke_gemini(prompt="Find auth code", model="gemini-3-flash")
26
+
27
+ # RIGHT: Agent can call semantic_search, grep, etc.
28
+ invoke_gemini_agentic(
29
+ prompt="Find auth code using semantic_search",
30
+ model="gemini-3-flash",
31
+ max_iterations=5
32
+ )
33
+ ```
34
+
35
+ ### 2. Verify Prerequisites
36
+
37
+ Some tools have prerequisites that must be met BEFORE use:
38
+
39
+ | Tool | Prerequisite | Check |
40
+ |------|--------------|-------|
41
+ | `semantic_search` | Index must exist | Run `semantic_index()` first |
42
+ | `lsp_*` | LSP server must be running | Check `lsp_servers()` |
43
+ | `invoke_*` | Auth must be configured | Run `stravinsky-auth status` |
44
+
45
+ **Add explicit checks**: Tools should fail EARLY with clear error messages if prerequisites are not met.
46
+
47
+ ### 3. Verify Parent Agent Guidance
48
+
49
+ Parent agents (orchestrators) must guide sub-agents to USE the tool:
50
+
51
+ | Level | Responsibility |
52
+ |-------|----------------|
53
+ | **Orchestrator** (stravinsky) | Tell sub-agents WHEN to use which tools |
54
+ | **Coordinator** (research-lead) | Tell explore/dewey HOW to use tools for query types |
55
+ | **Worker** (explore) | Actually CALL the tools |
56
+
57
+ **If a tool is available but never used**, check the guidance chain.
58
+
59
+ ### 4. Verification Pattern
60
+
61
+ After wiring a tool, verify the FULL chain:
62
+
63
+ ```
64
+ Step 1: Tool is in agent's tool list ✓
65
+ Step 2: Agent uses correct invocation ✓
66
+ Step 3: Prerequisites are met/checked ✓
67
+ Step 4: Parent agents guide usage ✓
68
+ Step 5: End-to-end test works ✓
69
+ ```
70
+
71
+ ## Example: semantic_search Integration (Fixed 2026-01-11)
72
+
73
+ **Original Problem**: explore agent had semantic_search in tool list but never used it.
74
+
75
+ **Root Causes Found**:
76
+ 1. explore.md called `invoke_gemini` (no tool access) instead of `invoke_gemini_agentic`
77
+ 2. semantic_search returned empty results when no index existed (silent failure)
78
+ 3. research-lead.md didn't tell explore agents WHEN to use semantic_search
79
+
80
+ **Fixes Applied**:
81
+ 1. Changed explore.md to use `invoke_gemini_agentic` with `max_iterations: 5`
82
+ 2. Added index existence check in semantic_search.py with clear error message
83
+ 3. Added "Pattern 3: Semantic/Conceptual Search" to research-lead.md
84
+
85
+ ## Rule
86
+
87
+ **When adding a tool to an agent, verify the FULL integration chain.**
88
+
89
+ Silent failures waste hours of debugging. Fail early, fail loudly.
@@ -0,0 +1,220 @@
1
+ # PyPI Deployment Rules
2
+
3
+ ## ⚠️ CRITICAL REMINDER: ALWAYS CLEAN BEFORE BUILDING
4
+
5
+ **The #1 deployment error:** Forgetting to clean dist/ before building
6
+
7
+ ```bash
8
+ # ALWAYS RUN THIS FUWT (use Python if hooks block rm):
9
+ python3 -c "import shutil; from pathlib import Path; [shutil.rmtree(p) for p in [Path('dist'), Path('build')] if p.exists()]; print('✅ Cleaned')"
10
+ ```
11
+
12
+ **Why:** PyPI rejects if dist/ contains old version files (e.g., 0.4.9.tar.gz when publishing 0.4.10)
13
+
14
+ ---
15
+
16
+ ## Version Management
17
+
18
+ 1. **ALWAYS check PyPI first before bumping version:**
19
+ ```bash
20
+ pip index versions stravinsky 2>&1 | head -20
21
+ ```
22
+ If the version you're about to use already exists on PyPI, bump higher!
23
+
24
+ 2. **Version must be consistent** across:
25
+ - `pyproject.toml` (line ~5): `version = "X.Y.Z"`
26
+ - `mcp_bridge/__init__.py`: `__version__ = "X.Y.Z"`
27
+
28
+ 3. **Version bumping strategy**:
29
+ - Patch (X.Y.Z+1): Bug fixes, documentation updates
30
+ - Minor (X.Y+1.0): New features, agent improvements, MCP tool additions
31
+ - Major (X+1.0.0): Breaking changes to API or architecture
32
+
33
+ 4. **⚠️ CRITICAL**: PyPI does NOT allow re-uploading the same version. If `uv publish` fails with "File already exists", you MUST bump the version number and try again.
34
+
35
+ ## CRITICAL RULES
36
+
37
+ 1. **Pin Python upper bounds when core dependencies require it**
38
+ - ✅ CORRECT: `requires-python = ">=3.11,<3.14"` (when chromadb/onnxruntime don't support 3.14+)
39
+ - ❌ WRONG: `requires-python = ">=3.11"` (allows installation on unsupported Python versions)
40
+ - Reason: Better to block installation than have broken runtime imports
41
+ - Current constraint: `<3.14` due to chromadb → onnxruntime lacking Python 3.14 wheels
42
+
43
+ 2. **ALWAYS install globally with @latest for auto-updates**
44
+ - ✅ CORRECT: `claude mcp add --global stravinsky -- uvx stravinsky@latest`
45
+ - ❌ WRONG: `claude mcp add stravinsky -- uvx stravinsky` (no @latest)
46
+ - ❌ WRONG: Local `.mcp.json` entries (use global only)
47
+
48
+ ## Pre-Deployment Checklist
49
+
50
+ Before deploying to PyPI, ensure:
51
+
52
+ 1. ✅ All changes committed to git
53
+ 2. ✅ Version numbers match in `pyproject.toml` and `mcp_bridge/__init__.py`
54
+ 3. ✅ No uncommitted temp files (`.stravinsky/agents/*.out`, `logs/`)
55
+ 4. ✅ New files properly tracked in git (check `.claude/agents/`, `docs/`)
56
+ 5. ✅ `uv.lock` is up-to-date
57
+ 6. ✅ **Python version constraint matches dependency requirements** (`<3.14` for chromadb)
58
+
59
+ ## Deployment Process
60
+
61
+ ### Step 1: Clean Build Artifacts (MANDATORY - DO NOT SKIP)
62
+
63
+ **⚠️ CRITICAL: ALWAYS clean dist/ before building, even if version was bumped!**
64
+
65
+ **Why this matters:**
66
+ - PyPI rejects uploads if a file with the same name already exists (even if version differs)
67
+ - Old build artifacts in dist/ from previous versions will cause 400 Bad Request errors
68
+ - `uv publish` uploads ALL files in dist/, not just the latest build
69
+
70
+ **WRONG:** ❌ Skip cleaning → build → publish (publishes old + new versions → ERROR)
71
+ **CORRECT:** ✅ Clean → build → publish (only publishes new version)
72
+
73
+ ```bash
74
+ # ⚠️ MANDATORY: Clean ALL build artifacts first
75
+ # If blocked by hooks, use Python alternative:
76
+ python3 -c "import shutil; from pathlib import Path; [shutil.rmtree(p) for p in [Path('dist'), Path('build')] if p.exists()]; print('✅ Cleaned dist/ and build/')"
77
+
78
+ # OR if hooks allow:
79
+ # rm -rf dist/ build/ *.egg-info
80
+
81
+ # Verify git status
82
+ git status
83
+
84
+ # Ensure version consistency (MUST match exactly)
85
+ VERSION_TOML=$(grep -E "^version = " pyproject.toml | head -1 | cut -d'"' -f2)
86
+ VERSION_INIT=$(grep -E "^__version__ = " mcp_bridge/__init__.py | cut -d'"' -f2)
87
+
88
+ if [ "$VERSION_TOML" != "$VERSION_INIT" ]; then
89
+ echo "❌ Version mismatch: pyproject.toml=$VERSION_TOML, __init__.py=$VERSION_INIT"
90
+ exit 1
91
+ fi
92
+
93
+ echo "✅ Version consistent: $VERSION_TOML"
94
+
95
+ # Verify dist/ is empty (CRITICAL CHECK)
96
+ if [ -d "dist" ] && [ "$(ls -A dist)" ]; then
97
+ echo "❌ ERROR: dist/ is not empty! Old artifacts will cause publish to fail."
98
+ echo " Files in dist/:"
99
+ ls -lh dist/
100
+ exit 1
101
+ fi
102
+
103
+ echo "✅ dist/ is clean"
104
+ ```
105
+
106
+ ### Step 2: Build (ONLY after cleaning)
107
+
108
+ ```bash
109
+ # Build with uv (creates dist/stravinsky-X.Y.Z.tar.gz and .whl)
110
+ uv build
111
+
112
+ # Verify correct version was built
113
+ ls -lh dist/
114
+ # Should ONLY show files with your new version number
115
+ ```
116
+
117
+ ### Step 3: Publish to PyPI
118
+
119
+ **CRITICAL: ALWAYS load .env file first!**
120
+
121
+ ```bash
122
+ # Load .env file (PYPI_API_TOKEN is stored here)
123
+ source .env
124
+
125
+ # Verify token is loaded
126
+ if [ -z "$PYPI_API_TOKEN" ]; then
127
+ echo "❌ PYPI_API_TOKEN not found in .env"
128
+ exit 1
129
+ fi
130
+
131
+ # Publish using PyPI API token from .env
132
+ uv publish --token "$PYPI_API_TOKEN"
133
+ ```
134
+
135
+ ### Step 4: Git Tag and Push
136
+
137
+ ```bash
138
+ # Create git tag
139
+ VERSION=$(grep -E "^version = " pyproject.toml | head -1 | cut -d'"' -f2)
140
+ git tag -a "v$VERSION" -m "chore: release v$VERSION"
141
+
142
+ # Push with tags
143
+ git push origin main --tags
144
+ ```
145
+
146
+ ### Step 5: Force uvx Cache Clear (MANDATORY)
147
+
148
+ **⚠️ CRITICAL: ALWAYS clear uvx cache after deployment!**
149
+
150
+ **Why this matters:**
151
+ - `@latest` doesn't force PyPI checks if uvx has a cached version
152
+ - Without clearing, users stay on old versions FOREVER
153
+ - This is THE most common cause of "version deployed but not updating" issues
154
+
155
+ ```bash
156
+ # Clear uvx cache to force fresh PyPI fetch on next Claude restart
157
+ python3 -c "import shutil; from pathlib import Path; cache = Path.home() / '.cache' / 'uv'; shutil.rmtree(cache, ignore_errors=True); print('✅ Cleared uvx cache - restart Claude Code to fetch v$(grep -E "^version = " pyproject.toml | head -1 | cut -d'"' -f2)')"
158
+
159
+ # Verify deployment is on PyPI
160
+ pip index versions stravinsky 2>&1 | head -5
161
+ ```
162
+
163
+ **Post-cache-clear:**
164
+ 1. Restart Claude Code (or wait for next restart)
165
+ 2. uvx will fetch the latest version from PyPI
166
+ 3. Verify with: run any stravinsky MCP tool and check version in output
167
+
168
+ ## Commit Message Convention
169
+
170
+ Use conventional commit format:
171
+
172
+ ```
173
+ <type>: <description>
174
+
175
+ [optional body]
176
+ ```
177
+
178
+ **Types:**
179
+ - `feat`: New feature
180
+ - `fix`: Bug fix
181
+ - `docs`: Documentation changes
182
+ - `chore`: Build/release tasks
183
+ - `refactor`: Code restructuring
184
+ - `test`: Test additions/changes
185
+
186
+ **Example:**
187
+ ```
188
+ feat: expand explore.md Multi-Model Usage from 12 to 319 lines
189
+
190
+ - Added 5 detailed examples with agent_context
191
+ - Documented gemini-3-flash usage patterns
192
+ - Added Model Selection Strategy section
193
+ - Documented haiku fallback behavior
194
+ - Added Gemini Best Practices (5 subsections)
195
+ ```
196
+
197
+ ## Emergency Rollback
198
+
199
+ If deployment fails:
200
+
201
+ ```bash
202
+ # Revert to previous version
203
+ git revert HEAD
204
+ git push origin main
205
+
206
+ # Delete failed tag
207
+ git tag -d v$VERSION
208
+ git push origin :refs/tags/v$VERSION
209
+ ```
210
+
211
+ ## Post-Deployment Verification
212
+
213
+ ```bash
214
+ # Verify PyPI package
215
+ pip install --upgrade stravinsky==$VERSION
216
+
217
+ # Check version
218
+ stravinsky --version
219
+ python -c "import mcp_bridge; print(mcp_bridge.__version__)"
220
+ ```
@@ -0,0 +1,32 @@
1
+ # Stravinsky Orchestrator Triggers
2
+
3
+ This rule ensures that the Stravinsky orchestrator persona and ULTRAWORK mode are correctly initiated when relevant keywords are used.
4
+
5
+ ## Triggers
6
+
7
+ - Keywords: `stravinsky`, `ultrawork`, `ulw`, `uw`
8
+ - Slash Commands: `/strav`, `/stravinsky`
9
+
10
+ ## Instructions
11
+
12
+ If the user initiates a task with any of the triggers above:
13
+
14
+ 1. **Assume the Stravinsky Persona**: You are a task orchestrator and parallel execution specialist.
15
+ 2. **Activate Parallel Mode**: You MUST use the `Task` tool for all specialized sub-tasks.
16
+ 3. **No Sequential Work**: If a task has 3+ independent steps, you MUST spawn subagents in parallel.
17
+ 4. **Verification**: Never trust a subagent result without verifying it (e.g., via `lsp_diagnostics` or tests).
18
+
19
+ ### ULTRAWORK Mode (ulw, uw)
20
+
21
+ If the `ULTRAWORK` (or `ulw`, `uw`) keyword is detected:
22
+ - Say "ULTRAWORK MODE ENABLED!" as your first response.
23
+ - Maximum parallelization is required (spawn all independent tasks immediately).
24
+ - Use a 32k thinking budget for planning.
25
+ - Deliver a full implementation, not a demo or skeleton.
26
+
27
+ ## Tool Enforcement
28
+
29
+ When Stravinsky mode is active:
30
+ - **BLOCKED**: Direct file reading/searching tools (`Read`, `Search`, `Grep`, `Bash`).
31
+ - **REQUIRED**: Use the `Task` tool to delegate to `explore`, `dewey`, `code-reviewer`, etc.
32
+ - **EXCEPTION**: You may use `TodoRead`/`TodoWrite` for planning.