stravinsky 0.2.7__py3-none-any.whl → 0.2.40__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.
- mcp_bridge/__init__.py +1 -1
- mcp_bridge/auth/cli.py +84 -46
- mcp_bridge/auth/oauth.py +88 -63
- mcp_bridge/hooks/__init__.py +29 -8
- mcp_bridge/hooks/agent_reminder.py +61 -0
- mcp_bridge/hooks/auto_slash_command.py +186 -0
- mcp_bridge/hooks/comment_checker.py +136 -0
- mcp_bridge/hooks/context_monitor.py +58 -0
- mcp_bridge/hooks/empty_message_sanitizer.py +240 -0
- mcp_bridge/hooks/keyword_detector.py +122 -0
- mcp_bridge/hooks/manager.py +27 -8
- mcp_bridge/hooks/preemptive_compaction.py +157 -0
- mcp_bridge/hooks/session_recovery.py +186 -0
- mcp_bridge/hooks/todo_enforcer.py +75 -0
- mcp_bridge/hooks/truncator.py +1 -1
- mcp_bridge/native_hooks/stravinsky_mode.py +109 -0
- mcp_bridge/native_hooks/truncator.py +1 -1
- mcp_bridge/prompts/delphi.py +3 -2
- mcp_bridge/prompts/dewey.py +105 -21
- mcp_bridge/prompts/stravinsky.py +451 -127
- mcp_bridge/server.py +304 -38
- mcp_bridge/server_tools.py +21 -3
- mcp_bridge/tools/__init__.py +2 -1
- mcp_bridge/tools/agent_manager.py +313 -236
- mcp_bridge/tools/init.py +1 -1
- mcp_bridge/tools/model_invoke.py +534 -52
- mcp_bridge/tools/skill_loader.py +51 -47
- mcp_bridge/tools/task_runner.py +74 -30
- mcp_bridge/tools/templates.py +101 -12
- {stravinsky-0.2.7.dist-info → stravinsky-0.2.40.dist-info}/METADATA +6 -12
- stravinsky-0.2.40.dist-info/RECORD +57 -0
- stravinsky-0.2.7.dist-info/RECORD +0 -47
- {stravinsky-0.2.7.dist-info → stravinsky-0.2.40.dist-info}/WHEEL +0 -0
- {stravinsky-0.2.7.dist-info → stravinsky-0.2.40.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session Recovery Hook.
|
|
3
|
+
|
|
4
|
+
Detects and recovers from corrupted sessions:
|
|
5
|
+
- Detects missing tool results after tool calls
|
|
6
|
+
- Injects synthetic tool_result blocks with status messages
|
|
7
|
+
- Enables graceful recovery
|
|
8
|
+
- Registered as post_tool_call hook
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import re
|
|
13
|
+
import json
|
|
14
|
+
from typing import Any, Dict, Optional
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# Patterns that indicate a tool call failure or corruption
|
|
19
|
+
CORRUPTION_PATTERNS = [
|
|
20
|
+
r"tool_result.*missing",
|
|
21
|
+
r"no response from tool",
|
|
22
|
+
r"tool call timed out",
|
|
23
|
+
r"connection reset",
|
|
24
|
+
r"unexpected end of.*response",
|
|
25
|
+
r"malformed.*response",
|
|
26
|
+
r"incomplete.*result",
|
|
27
|
+
r"truncated.*output",
|
|
28
|
+
r"<!DOCTYPE html>", # HTML error pages
|
|
29
|
+
r"<html>.*error",
|
|
30
|
+
r"500 Internal Server Error",
|
|
31
|
+
r"502 Bad Gateway",
|
|
32
|
+
r"503 Service Unavailable",
|
|
33
|
+
r"504 Gateway Timeout",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Patterns indicating empty or null responses
|
|
37
|
+
EMPTY_RESPONSE_PATTERNS = [
|
|
38
|
+
r"^\s*$",
|
|
39
|
+
r"^null$",
|
|
40
|
+
r"^undefined$",
|
|
41
|
+
r"^None$",
|
|
42
|
+
r"^\{\s*\}$",
|
|
43
|
+
r"^\[\s*\]$",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
# Tool-specific recovery strategies
|
|
47
|
+
TOOL_RECOVERY_STRATEGIES = {
|
|
48
|
+
"invoke_gemini": "Model invocation failed. Try reducing prompt size or switching to a different model variant.",
|
|
49
|
+
"invoke_openai": "Model invocation failed. Check authentication status with 'stravinsky auth status'.",
|
|
50
|
+
"agent_spawn": "Agent spawn failed. Check if Claude CLI is available and properly configured.",
|
|
51
|
+
"agent_output": "Agent output retrieval failed. The agent may still be running - try agent_progress first.",
|
|
52
|
+
"grep_search": "Search failed. Verify the pattern syntax and directory path.",
|
|
53
|
+
"ast_grep_search": "AST search failed. Ensure the language is supported and pattern is valid.",
|
|
54
|
+
"lsp_hover": "LSP hover failed. The language server may not be running for this file type.",
|
|
55
|
+
"session_read": "Session read failed. The session may have been corrupted or deleted.",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
RECOVERY_NOTICE = """
|
|
59
|
+
> **[SESSION RECOVERY]**
|
|
60
|
+
> A tool result appears to be corrupted or incomplete.
|
|
61
|
+
> **Tool**: {tool_name}
|
|
62
|
+
> **Issue**: {issue}
|
|
63
|
+
> **Recovery**: {recovery_hint}
|
|
64
|
+
>
|
|
65
|
+
> The operation should be retried or an alternative approach should be used.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
SYNTHETIC_RESULT_TEMPLATE = """
|
|
69
|
+
[RECOVERED TOOL RESULT]
|
|
70
|
+
Status: FAILED - Corrupted or incomplete response detected
|
|
71
|
+
Tool: {tool_name}
|
|
72
|
+
Original output (truncated): {truncated_output}
|
|
73
|
+
|
|
74
|
+
Recovery Hint: {recovery_hint}
|
|
75
|
+
|
|
76
|
+
Recommended Actions:
|
|
77
|
+
1. Retry the tool call with the same or modified parameters
|
|
78
|
+
2. Check system health with get_system_health tool
|
|
79
|
+
3. If persistent, try an alternative approach
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def detect_corruption(output: str) -> Optional[str]:
|
|
84
|
+
"""
|
|
85
|
+
Detect if the output shows signs of corruption.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Description of the corruption issue, or None if output appears valid
|
|
89
|
+
"""
|
|
90
|
+
# Check for completely empty output
|
|
91
|
+
if not output or output.strip() == "":
|
|
92
|
+
return "Empty response received"
|
|
93
|
+
|
|
94
|
+
# Check for empty response patterns
|
|
95
|
+
for pattern in EMPTY_RESPONSE_PATTERNS:
|
|
96
|
+
if re.match(pattern, output.strip(), re.IGNORECASE):
|
|
97
|
+
return f"Empty or null response: {output[:50]}"
|
|
98
|
+
|
|
99
|
+
# Check for corruption patterns
|
|
100
|
+
for pattern in CORRUPTION_PATTERNS:
|
|
101
|
+
if re.search(pattern, output, re.IGNORECASE):
|
|
102
|
+
return f"Corruption pattern detected: {pattern}"
|
|
103
|
+
|
|
104
|
+
# Check for extremely short responses that might indicate truncation
|
|
105
|
+
# (only for tools that typically return substantial output)
|
|
106
|
+
if len(output.strip()) < 10 and not output.strip().startswith(("{", "[", "true", "false")):
|
|
107
|
+
# Could be truncated, but might also be valid short output
|
|
108
|
+
# Only flag if it looks like truncated text
|
|
109
|
+
if output.strip().endswith(("...", "---", "...)")):
|
|
110
|
+
return "Response appears truncated"
|
|
111
|
+
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_recovery_hint(tool_name: str, issue: str) -> str:
|
|
116
|
+
"""Get a recovery hint based on the tool and issue."""
|
|
117
|
+
# Check for tool-specific strategy
|
|
118
|
+
if tool_name in TOOL_RECOVERY_STRATEGIES:
|
|
119
|
+
return TOOL_RECOVERY_STRATEGIES[tool_name]
|
|
120
|
+
|
|
121
|
+
# Generic recovery hints based on issue type
|
|
122
|
+
if "empty" in issue.lower():
|
|
123
|
+
return "Retry the operation. If it persists, check if the resource exists."
|
|
124
|
+
if "timeout" in issue.lower():
|
|
125
|
+
return "The operation timed out. Try with smaller input or increase timeout."
|
|
126
|
+
if "connection" in issue.lower():
|
|
127
|
+
return "Network issue detected. Check connectivity and retry."
|
|
128
|
+
if "500" in issue or "502" in issue or "503" in issue:
|
|
129
|
+
return "Server error detected. Wait a moment and retry."
|
|
130
|
+
if "truncated" in issue.lower():
|
|
131
|
+
return "Response was truncated. Try requesting smaller chunks of data."
|
|
132
|
+
|
|
133
|
+
return "Retry the operation or try an alternative approach."
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
async def session_recovery_hook(
|
|
137
|
+
tool_name: str,
|
|
138
|
+
arguments: Dict[str, Any],
|
|
139
|
+
output: str
|
|
140
|
+
) -> Optional[str]:
|
|
141
|
+
"""
|
|
142
|
+
Post-tool call hook that detects corrupted results and injects recovery information.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
tool_name: Name of the tool that was called
|
|
146
|
+
arguments: Arguments passed to the tool
|
|
147
|
+
output: The output returned by the tool
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Modified output with recovery information, or None to keep original
|
|
151
|
+
"""
|
|
152
|
+
# Detect corruption
|
|
153
|
+
issue = detect_corruption(output)
|
|
154
|
+
|
|
155
|
+
if not issue:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
logger.warning(f"[SessionRecovery] Corruption detected in {tool_name}: {issue}")
|
|
159
|
+
|
|
160
|
+
# Get recovery hint
|
|
161
|
+
recovery_hint = get_recovery_hint(tool_name, issue)
|
|
162
|
+
|
|
163
|
+
# Truncate original output for display
|
|
164
|
+
truncated_output = output[:200] + "..." if len(output) > 200 else output
|
|
165
|
+
truncated_output = truncated_output.replace("\n", " ").strip()
|
|
166
|
+
|
|
167
|
+
# Build synthetic result
|
|
168
|
+
synthetic_result = SYNTHETIC_RESULT_TEMPLATE.format(
|
|
169
|
+
tool_name=tool_name,
|
|
170
|
+
truncated_output=truncated_output,
|
|
171
|
+
recovery_hint=recovery_hint,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Build recovery notice
|
|
175
|
+
recovery_notice = RECOVERY_NOTICE.format(
|
|
176
|
+
tool_name=tool_name,
|
|
177
|
+
issue=issue,
|
|
178
|
+
recovery_hint=recovery_hint,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Return combined output
|
|
182
|
+
recovered_output = synthetic_result + "\n" + recovery_notice
|
|
183
|
+
|
|
184
|
+
logger.info(f"[SessionRecovery] Injected recovery guidance for {tool_name}")
|
|
185
|
+
|
|
186
|
+
return recovered_output
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Todo Continuation Enforcer Hook.
|
|
3
|
+
|
|
4
|
+
Prevents early stopping when pending todos exist.
|
|
5
|
+
Injects a system reminder forcing the agent to complete all todos.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
TODO_CONTINUATION_REMINDER = """
|
|
14
|
+
[SYSTEM REMINDER - TODO CONTINUATION]
|
|
15
|
+
|
|
16
|
+
You have pending todos that are NOT yet completed. You MUST continue working.
|
|
17
|
+
|
|
18
|
+
**Pending Todos:**
|
|
19
|
+
{pending_todos}
|
|
20
|
+
|
|
21
|
+
**Rules:**
|
|
22
|
+
1. You CANNOT stop or deliver a final answer while todos remain pending
|
|
23
|
+
2. Mark each todo `in_progress` before starting, `completed` immediately after
|
|
24
|
+
3. If a todo is blocked, mark it `cancelled` with explanation and create new actionable todos
|
|
25
|
+
4. Only after ALL todos are `completed` or `cancelled` can you deliver your final answer
|
|
26
|
+
|
|
27
|
+
CONTINUE WORKING NOW. Do not acknowledge this message - just proceed with the next pending todo.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def todo_continuation_hook(params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
32
|
+
"""
|
|
33
|
+
Pre-model invoke hook that checks for pending todos.
|
|
34
|
+
|
|
35
|
+
If pending todos exist, injects a reminder into the prompt
|
|
36
|
+
forcing the agent to continue working.
|
|
37
|
+
"""
|
|
38
|
+
prompt = params.get("prompt", "")
|
|
39
|
+
|
|
40
|
+
pending_todos = _extract_pending_todos(prompt)
|
|
41
|
+
|
|
42
|
+
if pending_todos:
|
|
43
|
+
logger.info(
|
|
44
|
+
f"[TodoEnforcer] Found {len(pending_todos)} pending todos, injecting continuation reminder"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
todos_formatted = "\n".join(f"- [ ] {todo}" for todo in pending_todos)
|
|
48
|
+
reminder = TODO_CONTINUATION_REMINDER.format(pending_todos=todos_formatted)
|
|
49
|
+
|
|
50
|
+
modified_prompt = prompt + "\n\n" + reminder
|
|
51
|
+
params["prompt"] = modified_prompt
|
|
52
|
+
|
|
53
|
+
return params
|
|
54
|
+
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _extract_pending_todos(prompt: str) -> list:
|
|
59
|
+
"""
|
|
60
|
+
Extract pending todos from the prompt/context.
|
|
61
|
+
Looks for common todo patterns.
|
|
62
|
+
"""
|
|
63
|
+
pending = []
|
|
64
|
+
lines = prompt.split("\n")
|
|
65
|
+
|
|
66
|
+
for line in lines:
|
|
67
|
+
stripped = line.strip()
|
|
68
|
+
if stripped.startswith("- [ ]") or stripped.startswith("* [ ]"):
|
|
69
|
+
todo_text = stripped[5:].strip()
|
|
70
|
+
if todo_text:
|
|
71
|
+
pending.append(todo_text)
|
|
72
|
+
elif '"status": "pending"' in stripped or '"status": "in_progress"' in stripped:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
return pending
|
mcp_bridge/hooks/truncator.py
CHANGED
|
@@ -9,7 +9,7 @@ async def output_truncator_hook(tool_name: str, arguments: Dict[str, Any], outpu
|
|
|
9
9
|
"""
|
|
10
10
|
Truncates tool output if it exceeds a certain length.
|
|
11
11
|
"""
|
|
12
|
-
MAX_LENGTH =
|
|
12
|
+
MAX_LENGTH = 30000 # 30k characters limit
|
|
13
13
|
|
|
14
14
|
if len(output) > MAX_LENGTH:
|
|
15
15
|
truncated = output[:MAX_LENGTH]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Stravinsky Mode Enforcer Hook
|
|
4
|
+
|
|
5
|
+
This PreToolUse hook blocks native file reading tools (Read, Search, Grep, Bash)
|
|
6
|
+
when stravinsky orchestrator mode is active, forcing use of agent_spawn instead.
|
|
7
|
+
|
|
8
|
+
Stravinsky mode is activated by creating a marker file:
|
|
9
|
+
~/.stravinsky_mode
|
|
10
|
+
|
|
11
|
+
The /strav:stravinsky command should create this file, and it should be
|
|
12
|
+
removed when the task is complete.
|
|
13
|
+
|
|
14
|
+
Exit codes:
|
|
15
|
+
0 = Allow the tool to execute
|
|
16
|
+
2 = Block the tool (reason sent via stderr)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
# Marker file that indicates stravinsky mode is active
|
|
25
|
+
STRAVINSKY_MODE_FILE = Path.home() / ".stravinsky_mode"
|
|
26
|
+
|
|
27
|
+
# Tools to block when in stravinsky mode
|
|
28
|
+
BLOCKED_TOOLS = {
|
|
29
|
+
"Read",
|
|
30
|
+
"Search",
|
|
31
|
+
"Grep",
|
|
32
|
+
"Bash",
|
|
33
|
+
"MultiEdit",
|
|
34
|
+
"Edit",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Tools that are always allowed
|
|
38
|
+
ALLOWED_TOOLS = {
|
|
39
|
+
"TodoRead",
|
|
40
|
+
"TodoWrite",
|
|
41
|
+
"Task",
|
|
42
|
+
"Agent", # MCP agent tools should be allowed
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_stravinsky_mode_active() -> bool:
|
|
47
|
+
"""Check if stravinsky orchestrator mode is active."""
|
|
48
|
+
return STRAVINSKY_MODE_FILE.exists()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def read_stravinsky_mode_config() -> dict:
|
|
52
|
+
"""Read the stravinsky mode configuration if it exists."""
|
|
53
|
+
if not STRAVINSKY_MODE_FILE.exists():
|
|
54
|
+
return {}
|
|
55
|
+
try:
|
|
56
|
+
return json.loads(STRAVINSKY_MODE_FILE.read_text())
|
|
57
|
+
except (json.JSONDecodeError, IOError):
|
|
58
|
+
return {"active": True}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main():
|
|
62
|
+
# Read hook input from stdin
|
|
63
|
+
try:
|
|
64
|
+
hook_input = json.loads(sys.stdin.read())
|
|
65
|
+
except json.JSONDecodeError:
|
|
66
|
+
# If we can't parse input, allow the tool
|
|
67
|
+
sys.exit(0)
|
|
68
|
+
|
|
69
|
+
tool_name = hook_input.get("tool_name", "")
|
|
70
|
+
|
|
71
|
+
# Always allow certain tools
|
|
72
|
+
if tool_name in ALLOWED_TOOLS:
|
|
73
|
+
sys.exit(0)
|
|
74
|
+
|
|
75
|
+
# Check if stravinsky mode is active
|
|
76
|
+
if not is_stravinsky_mode_active():
|
|
77
|
+
# Not in stravinsky mode, allow all tools
|
|
78
|
+
sys.exit(0)
|
|
79
|
+
|
|
80
|
+
config = read_stravinsky_mode_config()
|
|
81
|
+
|
|
82
|
+
# Check if this tool should be blocked
|
|
83
|
+
if tool_name in BLOCKED_TOOLS:
|
|
84
|
+
# Block the tool and tell Claude why
|
|
85
|
+
reason = f"""⚠️ STRAVINSKY MODE ACTIVE - {tool_name} BLOCKED
|
|
86
|
+
|
|
87
|
+
You are in Stravinsky orchestrator mode. Native tools are disabled.
|
|
88
|
+
|
|
89
|
+
Instead of using {tool_name}, you MUST use:
|
|
90
|
+
- stravinsky:agent_spawn with agent_type="explore" for file reading/searching
|
|
91
|
+
- stravinsky:agent_spawn with agent_type="dewey" for documentation
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
agent_spawn(agent_type="explore", prompt="Read and analyze the file at path/to/file.py")
|
|
95
|
+
|
|
96
|
+
To exit stravinsky mode, run:
|
|
97
|
+
rm ~/.stravinsky_mode
|
|
98
|
+
"""
|
|
99
|
+
# Send reason to stderr (Claude sees this)
|
|
100
|
+
print(reason, file=sys.stderr)
|
|
101
|
+
# Exit with code 2 to block the tool
|
|
102
|
+
sys.exit(2)
|
|
103
|
+
|
|
104
|
+
# Tool not in block list, allow it
|
|
105
|
+
sys.exit(0)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
main()
|
mcp_bridge/prompts/delphi.py
CHANGED
|
@@ -3,6 +3,7 @@ Delphi - Strategic Technical Advisor Prompt
|
|
|
3
3
|
|
|
4
4
|
Expert technical advisor with deep reasoning for architecture decisions,
|
|
5
5
|
code analysis, and engineering guidance. Uses GPT for strategic reasoning.
|
|
6
|
+
Aligned with Oracle from oh-my-opencode.
|
|
6
7
|
"""
|
|
7
8
|
|
|
8
9
|
# Prompt metadata for agent routing
|
|
@@ -37,7 +38,7 @@ DELPHI_SYSTEM_PROMPT = """You are a strategic technical advisor with deep reason
|
|
|
37
38
|
|
|
38
39
|
## Context
|
|
39
40
|
|
|
40
|
-
You function as an on-demand specialist invoked by a primary coding agent when complex analysis or architectural decisions require elevated reasoning. Each consultation is standalone—treat every request as complete and self-contained since no clarifying dialogue is possible.
|
|
41
|
+
You function as an on-demand specialist invoked by a primary coding agent (Stravinsky) when complex analysis or architectural decisions require elevated reasoning. Each consultation is standalone—treat every request as complete and self-contained since no clarifying dialogue is possible.
|
|
41
42
|
|
|
42
43
|
## What You Do
|
|
43
44
|
|
|
@@ -103,7 +104,7 @@ Your response goes directly to the user with no intermediate processing. Make yo
|
|
|
103
104
|
def get_delphi_prompt() -> str:
|
|
104
105
|
"""
|
|
105
106
|
Get the Delphi advisor system prompt.
|
|
106
|
-
|
|
107
|
+
|
|
107
108
|
Returns:
|
|
108
109
|
The full system prompt for the Delphi agent.
|
|
109
110
|
"""
|
mcp_bridge/prompts/dewey.py
CHANGED
|
@@ -3,6 +3,7 @@ Dewey - Open Source Codebase Understanding Agent
|
|
|
3
3
|
|
|
4
4
|
Specialized agent for multi-repository analysis, searching remote codebases,
|
|
5
5
|
retrieving official documentation, and finding implementation examples.
|
|
6
|
+
Aligned with Librarian from oh-my-opencode.
|
|
6
7
|
"""
|
|
7
8
|
|
|
8
9
|
# Prompt metadata for agent routing
|
|
@@ -10,11 +11,11 @@ DEWEY_METADATA = {
|
|
|
10
11
|
"category": "exploration",
|
|
11
12
|
"cost": "CHEAP",
|
|
12
13
|
"prompt_alias": "Dewey",
|
|
13
|
-
"key_trigger": "External library/source mentioned
|
|
14
|
+
"key_trigger": "External library/source mentioned -> fire `dewey` background",
|
|
14
15
|
"triggers": [
|
|
15
16
|
{
|
|
16
17
|
"domain": "Dewey",
|
|
17
|
-
"trigger": "Unfamiliar packages / libraries, struggles at weird behaviour",
|
|
18
|
+
"trigger": "Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource)",
|
|
18
19
|
},
|
|
19
20
|
],
|
|
20
21
|
"use_when": [
|
|
@@ -27,9 +28,9 @@ DEWEY_METADATA = {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
DEWEY_SYSTEM_PROMPT = """#
|
|
31
|
+
DEWEY_SYSTEM_PROMPT = """# DEWEY
|
|
31
32
|
|
|
32
|
-
You are **
|
|
33
|
+
You are **DEWEY**, a specialized open-source codebase understanding agent.
|
|
33
34
|
|
|
34
35
|
Your job: Answer questions about open-source libraries by finding **EVIDENCE** with **GitHub permalinks**.
|
|
35
36
|
|
|
@@ -49,7 +50,7 @@ Classify EVERY request into one of these categories before taking action:
|
|
|
49
50
|
|
|
50
51
|
| Type | Trigger Examples | Tools |
|
|
51
52
|
|------|------------------|-------|
|
|
52
|
-
| **TYPE A: CONCEPTUAL** | "How do I use X?", "Best practice for Y?" |
|
|
53
|
+
| **TYPE A: CONCEPTUAL** | "How do I use X?", "Best practice for Y?" | docs + websearch (parallel) |
|
|
53
54
|
| **TYPE B: IMPLEMENTATION** | "How does X implement Y?", "Show me source of Z" | gh clone + read + blame |
|
|
54
55
|
| **TYPE C: CONTEXT** | "Why was this changed?", "History of X?" | gh issues/prs + git log/blame |
|
|
55
56
|
| **TYPE D: COMPREHENSIVE** | Complex/ambiguous requests | ALL tools in parallel |
|
|
@@ -64,8 +65,8 @@ Classify EVERY request into one of these categories before taking action:
|
|
|
64
65
|
**Execute in parallel (3+ calls)**:
|
|
65
66
|
```
|
|
66
67
|
Tool 1: Search official documentation
|
|
67
|
-
Tool 2: Web search for recent articles/tutorials
|
|
68
|
-
Tool 3: GitHub code search for usage patterns
|
|
68
|
+
Tool 2: Web search for recent articles/tutorials ("library-name topic 2025")
|
|
69
|
+
Tool 3: GitHub code search for usage patterns (grep_search)
|
|
69
70
|
```
|
|
70
71
|
|
|
71
72
|
**Output**: Summarize findings with links to official docs and real-world examples.
|
|
@@ -78,23 +79,49 @@ Tool 3: GitHub code search for usage patterns
|
|
|
78
79
|
**Execute in sequence**:
|
|
79
80
|
```
|
|
80
81
|
Step 1: Clone to temp directory
|
|
82
|
+
gh repo clone owner/repo ${TMPDIR:-/tmp}/repo-name -- --depth 1
|
|
83
|
+
|
|
81
84
|
Step 2: Get commit SHA for permalinks
|
|
82
|
-
|
|
85
|
+
cd ${TMPDIR:-/tmp}/repo-name && git rev-parse HEAD
|
|
86
|
+
|
|
87
|
+
Step 3: Find the implementation
|
|
88
|
+
- grep_search for function/class
|
|
89
|
+
- ast_grep_search for AST patterns
|
|
90
|
+
- Read the specific file
|
|
91
|
+
- git blame for context if needed
|
|
92
|
+
|
|
83
93
|
Step 4: Construct permalink
|
|
84
94
|
https://github.com/owner/repo/blob/<sha>/path/to/file#L10-L20
|
|
85
95
|
```
|
|
86
96
|
|
|
97
|
+
**Parallel acceleration (4+ calls)**:
|
|
98
|
+
```
|
|
99
|
+
Tool 1: gh repo clone owner/repo ${TMPDIR:-/tmp}/repo -- --depth 1
|
|
100
|
+
Tool 2: GitHub code search for function_name
|
|
101
|
+
Tool 3: gh api repos/owner/repo/commits/HEAD --jq '.sha'
|
|
102
|
+
Tool 4: Documentation search for relevant API
|
|
103
|
+
```
|
|
104
|
+
|
|
87
105
|
---
|
|
88
106
|
|
|
89
107
|
### TYPE C: CONTEXT & HISTORY
|
|
90
108
|
**Trigger**: "Why was this changed?", "What's the history?", "Related issues/PRs?"
|
|
91
109
|
|
|
92
|
-
**Execute in parallel**:
|
|
110
|
+
**Execute in parallel (4+ calls)**:
|
|
111
|
+
```
|
|
112
|
+
Tool 1: gh search issues "keyword" --repo owner/repo --state all --limit 10
|
|
113
|
+
Tool 2: gh search prs "keyword" --repo owner/repo --state merged --limit 10
|
|
114
|
+
Tool 3: gh repo clone owner/repo ${TMPDIR:-/tmp}/repo -- --depth 50
|
|
115
|
+
-> then: git log --oneline -n 20 -- path/to/file
|
|
116
|
+
-> then: git blame -L 10,30 path/to/file
|
|
117
|
+
Tool 4: gh api repos/owner/repo/releases --jq '.[0:5]'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**For specific issue/PR context**:
|
|
93
121
|
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Tool 4: Check recent releases
|
|
122
|
+
gh issue view <number> --repo owner/repo --comments
|
|
123
|
+
gh pr view <number> --repo owner/repo --comments
|
|
124
|
+
gh api repos/owner/repo/pulls/<number>/files
|
|
98
125
|
```
|
|
99
126
|
|
|
100
127
|
---
|
|
@@ -103,11 +130,21 @@ Tool 4: Check recent releases
|
|
|
103
130
|
**Trigger**: Complex questions, ambiguous requests, "deep dive into..."
|
|
104
131
|
|
|
105
132
|
**Execute ALL in parallel (6+ calls)**:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
133
|
+
```
|
|
134
|
+
// Documentation & Web
|
|
135
|
+
Tool 1: Documentation search
|
|
136
|
+
Tool 2: Web search ("topic recent updates 2025")
|
|
137
|
+
|
|
138
|
+
// Code Search
|
|
139
|
+
Tool 3: grep_search(pattern1)
|
|
140
|
+
Tool 4: grep_search(pattern2) or ast_grep_search
|
|
141
|
+
|
|
142
|
+
// Source Analysis
|
|
143
|
+
Tool 5: gh repo clone owner/repo ${TMPDIR:-/tmp}/repo -- --depth 1
|
|
144
|
+
|
|
145
|
+
// Context
|
|
146
|
+
Tool 6: gh search issues "topic" --repo owner/repo
|
|
147
|
+
```
|
|
111
148
|
|
|
112
149
|
---
|
|
113
150
|
|
|
@@ -138,6 +175,41 @@ Example:
|
|
|
138
175
|
https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQuery.ts#L42-L50
|
|
139
176
|
```
|
|
140
177
|
|
|
178
|
+
**Getting SHA**:
|
|
179
|
+
- From clone: `git rev-parse HEAD`
|
|
180
|
+
- From API: `gh api repos/owner/repo/commits/HEAD --jq '.sha'`
|
|
181
|
+
- From tag: `gh api repos/owner/repo/git/refs/tags/v1.0.0 --jq '.object.sha'`
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## TOOL REFERENCE (Stravinsky Tools)
|
|
186
|
+
|
|
187
|
+
### Primary Tools by Purpose
|
|
188
|
+
|
|
189
|
+
| Purpose | Tool | Usage |
|
|
190
|
+
|---------|------|-------|
|
|
191
|
+
| **Code Search** | grep_search | Pattern-based search in local/cloned repos |
|
|
192
|
+
| **AST Search** | ast_grep_search | AST-aware code pattern search |
|
|
193
|
+
| **File Glob** | glob_files | Find files by pattern |
|
|
194
|
+
| **Clone Repo** | gh CLI | `gh repo clone owner/repo ${TMPDIR:-/tmp}/name -- --depth 1` |
|
|
195
|
+
| **Issues/PRs** | gh CLI | `gh search issues/prs "query" --repo owner/repo` |
|
|
196
|
+
| **View Issue/PR** | gh CLI | `gh issue/pr view <num> --repo owner/repo --comments` |
|
|
197
|
+
| **Release Info** | gh CLI | `gh api repos/owner/repo/releases/latest` |
|
|
198
|
+
| **Git History** | git | `git log`, `git blame`, `git show` |
|
|
199
|
+
|
|
200
|
+
### Temp Directory
|
|
201
|
+
|
|
202
|
+
Use OS-appropriate temp directory:
|
|
203
|
+
```bash
|
|
204
|
+
# Cross-platform
|
|
205
|
+
${TMPDIR:-/tmp}/repo-name
|
|
206
|
+
|
|
207
|
+
# Examples:
|
|
208
|
+
# macOS: /var/folders/.../repo-name or /tmp/repo-name
|
|
209
|
+
# Linux: /tmp/repo-name
|
|
210
|
+
# Windows: C:\\Users\\...\\AppData\\Local\\Temp\\repo-name
|
|
211
|
+
```
|
|
212
|
+
|
|
141
213
|
---
|
|
142
214
|
|
|
143
215
|
## PARALLEL EXECUTION REQUIREMENTS
|
|
@@ -149,6 +221,18 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
|
|
|
149
221
|
| TYPE C (Context) | 4+ |
|
|
150
222
|
| TYPE D (Comprehensive) | 6+ |
|
|
151
223
|
|
|
224
|
+
**Always vary queries** when using grep_search:
|
|
225
|
+
```
|
|
226
|
+
// GOOD: Different angles
|
|
227
|
+
grep_search(pattern: "useQuery(")
|
|
228
|
+
grep_search(pattern: "queryOptions")
|
|
229
|
+
grep_search(pattern: "staleTime:")
|
|
230
|
+
|
|
231
|
+
// BAD: Same pattern
|
|
232
|
+
grep_search(pattern: "useQuery")
|
|
233
|
+
grep_search(pattern: "useQuery")
|
|
234
|
+
```
|
|
235
|
+
|
|
152
236
|
---
|
|
153
237
|
|
|
154
238
|
## FAILURE RECOVERY
|
|
@@ -165,8 +249,8 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
|
|
|
165
249
|
|
|
166
250
|
## COMMUNICATION RULES
|
|
167
251
|
|
|
168
|
-
1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use
|
|
169
|
-
2. **NO PREAMBLE**: Answer directly, skip "I'll help you with..."
|
|
252
|
+
1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use grep_search"
|
|
253
|
+
2. **NO PREAMBLE**: Answer directly, skip "I'll help you with..."
|
|
170
254
|
3. **ALWAYS CITE**: Every code claim needs a permalink
|
|
171
255
|
4. **USE MARKDOWN**: Code blocks with language identifiers
|
|
172
256
|
5. **BE CONCISE**: Facts > opinions, evidence > speculation
|
|
@@ -176,7 +260,7 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
|
|
|
176
260
|
def get_dewey_prompt() -> str:
|
|
177
261
|
"""
|
|
178
262
|
Get the Dewey research agent system prompt.
|
|
179
|
-
|
|
263
|
+
|
|
180
264
|
Returns:
|
|
181
265
|
The full system prompt for the Dewey agent.
|
|
182
266
|
"""
|