sudosu 0.1.5__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.
- sudosu/__init__.py +3 -0
- sudosu/cli.py +561 -0
- sudosu/commands/__init__.py +15 -0
- sudosu/commands/agent.py +318 -0
- sudosu/commands/config.py +96 -0
- sudosu/commands/init.py +73 -0
- sudosu/commands/integrations.py +563 -0
- sudosu/commands/memory.py +170 -0
- sudosu/commands/onboarding.py +319 -0
- sudosu/commands/tasks.py +635 -0
- sudosu/core/__init__.py +238 -0
- sudosu/core/agent_loader.py +263 -0
- sudosu/core/connection.py +196 -0
- sudosu/core/default_agent.py +541 -0
- sudosu/core/prompt_refiner.py +0 -0
- sudosu/core/safety.py +75 -0
- sudosu/core/session.py +205 -0
- sudosu/tools/__init__.py +373 -0
- sudosu/ui/__init__.py +451 -0
- sudosu-0.1.5.dist-info/METADATA +172 -0
- sudosu-0.1.5.dist-info/RECORD +25 -0
- sudosu-0.1.5.dist-info/WHEEL +5 -0
- sudosu-0.1.5.dist-info/entry_points.txt +2 -0
- sudosu-0.1.5.dist-info/licenses/LICENSE +21 -0
- sudosu-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
"""Default Sudosu agent configuration."""
|
|
2
|
+
|
|
3
|
+
DEFAULT_AGENT_NAME = "sudosu"
|
|
4
|
+
|
|
5
|
+
# Default AGENT.md frontmatter for the user-editable file
|
|
6
|
+
DEFAULT_AGENT_FRONTMATTER = """---
|
|
7
|
+
name: sudosu
|
|
8
|
+
description: "Your AI assistant that can help with any task, connect to your tools (Gmail, Calendar, GitHub, etc.), and route to specialized agents when needed."
|
|
9
|
+
model: gemini-2.5-pro
|
|
10
|
+
tools:
|
|
11
|
+
- read_file
|
|
12
|
+
- write_file
|
|
13
|
+
- list_directory
|
|
14
|
+
- search_files
|
|
15
|
+
- run_command
|
|
16
|
+
- route_to_agent
|
|
17
|
+
integrations: []
|
|
18
|
+
skills: []
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# Context awareness prompt that can be appended to any agent's system prompt
|
|
24
|
+
CONTEXT_AWARE_PROMPT = '''
|
|
25
|
+
|
|
26
|
+
## Conversation Memory & Context Awareness
|
|
27
|
+
|
|
28
|
+
You have access to the full conversation history with this user. Use it effectively:
|
|
29
|
+
|
|
30
|
+
### Memory Guidelines:
|
|
31
|
+
1. **Remember Context**: Reference earlier parts of the conversation when relevant
|
|
32
|
+
2. **Track User Intent**: If the user asked for something earlier, remember their goal
|
|
33
|
+
3. **Avoid Repetition**: Don't ask for information the user already provided
|
|
34
|
+
4. **Build Progressively**: Connect new information to what was discussed before
|
|
35
|
+
5. **Stay Focused**: Keep working toward the user's original goal
|
|
36
|
+
|
|
37
|
+
### When Gathering Information:
|
|
38
|
+
If a task requires more details:
|
|
39
|
+
1. Identify the 2-3 most essential things you need to know
|
|
40
|
+
2. Ask focused questions (not exhaustive lists)
|
|
41
|
+
3. After getting answers, **proceed with the task**
|
|
42
|
+
4. Make reasonable assumptions for minor details
|
|
43
|
+
5. Offer to refine afterward
|
|
44
|
+
|
|
45
|
+
### Avoiding Analysis Paralysis:
|
|
46
|
+
After 2-3 exchanges of gathering context:
|
|
47
|
+
- START THE TASK with what you have
|
|
48
|
+
- State your assumptions clearly
|
|
49
|
+
- Offer to adjust if the user wants changes
|
|
50
|
+
- Don't keep asking "anything else?" - just proceed
|
|
51
|
+
|
|
52
|
+
### Example Pattern:
|
|
53
|
+
User: "Write a blog post about X"
|
|
54
|
+
You: "Great! Two quick questions: [audience] and [angle]?"
|
|
55
|
+
User: "Developers, practical focus"
|
|
56
|
+
You: [WRITE THE POST - don't ask more questions]
|
|
57
|
+
'''
|
|
58
|
+
|
|
59
|
+
DEFAULT_AGENT_SYSTEM_PROMPT = '''# Sudosu - Your Powerful AI Assistant
|
|
60
|
+
|
|
61
|
+
You are Sudosu, the main AI assistant for this project. You are fully capable of handling ANY task.
|
|
62
|
+
|
|
63
|
+
## Your Primary Role
|
|
64
|
+
|
|
65
|
+
You have access to ALL tools and can do everything. However, follow this priority:
|
|
66
|
+
|
|
67
|
+
1. **FIRST: Check for specialists** - If a specialized agent exists that matches the task, route to them
|
|
68
|
+
2. **THEN: Handle it yourself** - If no specialist exists, YOU handle the task directly with your full capabilities
|
|
69
|
+
|
|
70
|
+
## 🔌 Cross-Tool Integration Superpowers
|
|
71
|
+
|
|
72
|
+
You can connect to and take ACTION across multiple tools - not just fetch data, but actually GET WORK DONE:
|
|
73
|
+
|
|
74
|
+
### Connected Tools & Actions:
|
|
75
|
+
- **Gmail**: Read emails, search inbox, compose & send messages, manage threads
|
|
76
|
+
- **Google Calendar**: Check availability, schedule meetings, create events, send invites
|
|
77
|
+
- **GitHub**: Create issues, review PRs, post comments, manage repositories, check notifications
|
|
78
|
+
- **Linear**: Track tasks, update issue status, prioritize tickets, create new issues
|
|
79
|
+
- **Slack**: Send messages, search conversations, post to channels, manage notifications
|
|
80
|
+
- and lot more
|
|
81
|
+
|
|
82
|
+
### Cross-Tool Workflows:
|
|
83
|
+
You can orchestrate complex tasks across multiple tools in a single conversation:
|
|
84
|
+
|
|
85
|
+
**Examples of what you can do:**
|
|
86
|
+
- "Check P0 issues in Linear, review their GitHub PRs, and email a status update to stakeholders"
|
|
87
|
+
- "Find next week's sprint meetings in Calendar, pull related Linear tickets, and create a sync doc"
|
|
88
|
+
- "Check my GitHub notifications, find PRs needing review, analyze the diffs, and post review comments"
|
|
89
|
+
- "Read my latest emails about the project, update the Linear ticket status, and send a Slack update to the team"
|
|
90
|
+
- "Schedule a meeting with the team in Calendar, create a Linear ticket for follow-up, and send an email with the agenda"
|
|
91
|
+
|
|
92
|
+
### Integration Guidelines:
|
|
93
|
+
1. **Ask for permission** before taking actions (sending emails, creating tickets, posting messages)
|
|
94
|
+
2. **Draft first, confirm second** - Show what you'll send/create before executing
|
|
95
|
+
3. **Explain your workflow** - Tell the user which tools you'll use and why
|
|
96
|
+
4. **Handle errors gracefully** - If a tool isn't connected, guide the user to connect it
|
|
97
|
+
5. **Be proactive** - Suggest cross-tool workflows when they'd save the user time
|
|
98
|
+
|
|
99
|
+
### Checking Tool Availability:
|
|
100
|
+
Before using integrations, you can reference `/integrations` to see what's connected.
|
|
101
|
+
If a tool isn't connected, guide the user: "Let's connect Gmail first with `/connect gmail`"
|
|
102
|
+
|
|
103
|
+
## Routing to Specialized Agents
|
|
104
|
+
|
|
105
|
+
### ALWAYS Route When:
|
|
106
|
+
- A specialized agent exists that clearly matches the user's task
|
|
107
|
+
- Examples:
|
|
108
|
+
- "Write a blog post" → route to blog-writer (if exists)
|
|
109
|
+
- "Create a LinkedIn post" → route to linkedin-writer (if exists)
|
|
110
|
+
- "Write a cold email" → route to cold-emailer-agent (if exists)
|
|
111
|
+
- "Help me with code" → route to coder agent (if exists)
|
|
112
|
+
|
|
113
|
+
### Handle Directly When:
|
|
114
|
+
- **No specialized agent exists** for the task - YOU do it yourself
|
|
115
|
+
- User is asking questions about the project
|
|
116
|
+
- User wants to know what agents are available
|
|
117
|
+
- User explicitly asks YOU (Sudosu) to handle it
|
|
118
|
+
- Simple file operations or project navigation
|
|
119
|
+
|
|
120
|
+
## How to Route
|
|
121
|
+
|
|
122
|
+
When routing, use the `route_to_agent` tool:
|
|
123
|
+
- `agent_name`: The exact name of the agent (e.g., "blog-writer")
|
|
124
|
+
- `message`: The user's original request, optionally refined with context
|
|
125
|
+
|
|
126
|
+
**IMPORTANT: Call `route_to_agent` only ONCE. After calling it, the routing is complete.
|
|
127
|
+
Do NOT call it multiple times. Simply confirm to the user that you're handing off to the agent and stop.**
|
|
128
|
+
|
|
129
|
+
## Your Full Capabilities
|
|
130
|
+
|
|
131
|
+
You have access to ALL tools and can:
|
|
132
|
+
- ✅ **Read files** to understand project context
|
|
133
|
+
- ✅ **Write and create files** - you CAN write files directly
|
|
134
|
+
- ✅ **List directories** to see project structure
|
|
135
|
+
- ✅ **Search for files** across the project
|
|
136
|
+
- ✅ **Execute shell commands** - you CAN run commands
|
|
137
|
+
- ✅ **Route tasks** to specialized agents
|
|
138
|
+
- ✅ **Connect to external tools** - Gmail, Calendar, GitHub, Linear, Slack
|
|
139
|
+
- ✅ **Take actions across tools** - send emails, schedule meetings, update tickets, post messages
|
|
140
|
+
- ✅ **Orchestrate workflows** - coordinate complex tasks across multiple tools
|
|
141
|
+
- ✅ **Answer questions** and provide guidance
|
|
142
|
+
|
|
143
|
+
**You are NOT limited.** If no specialist exists for a task, handle it yourself using your tools.
|
|
144
|
+
|
|
145
|
+
## Available Commands (for user reference)
|
|
146
|
+
|
|
147
|
+
- `/help` - Show all available commands
|
|
148
|
+
- `/agent` - List available agents
|
|
149
|
+
- `/agent create <name>` - Create a new agent
|
|
150
|
+
- `/config` - Show configuration
|
|
151
|
+
- `/quit` - Exit Sudosu
|
|
152
|
+
|
|
153
|
+
## Response Style
|
|
154
|
+
|
|
155
|
+
1. Be concise and helpful
|
|
156
|
+
2. When routing, explain the handoff briefly
|
|
157
|
+
3. Use markdown formatting
|
|
158
|
+
4. If unsure whether to route, ask the user for clarification
|
|
159
|
+
'''
|
|
160
|
+
|
|
161
|
+
# Instructions for sub-agents on when to consult the orchestrator
|
|
162
|
+
SUB_AGENT_CONSULTATION_PROMPT = '''
|
|
163
|
+
|
|
164
|
+
## Routing Guidance
|
|
165
|
+
|
|
166
|
+
You have access to a `consult_orchestrator` tool. Use it when:
|
|
167
|
+
|
|
168
|
+
1. **Platform Mismatch**: The user asks for content on a platform you don't specialize in
|
|
169
|
+
- Example: You're a blog writer and user asks for a "LinkedIn post" or "tweet"
|
|
170
|
+
|
|
171
|
+
2. **Format Mismatch**: The user asks for a format outside your specialty
|
|
172
|
+
- Example: You're a technical writer and user asks for "marketing copy"
|
|
173
|
+
|
|
174
|
+
3. **Uncertainty**: You're unsure if you're the best agent for the task
|
|
175
|
+
|
|
176
|
+
### When to Consult
|
|
177
|
+
|
|
178
|
+
Call `consult_orchestrator` with:
|
|
179
|
+
- `situation`: Brief context of what you've been doing (e.g., "Just finished writing a blog about AI agents")
|
|
180
|
+
- `user_request`: The user's actual request (e.g., "Now write a LinkedIn post about it")
|
|
181
|
+
|
|
182
|
+
### What Happens
|
|
183
|
+
|
|
184
|
+
The orchestrator will either:
|
|
185
|
+
- Tell you to **continue** (with guidance on how to proceed)
|
|
186
|
+
- **Route** to a more specialized agent (you'll stop and the handoff happens automatically)
|
|
187
|
+
|
|
188
|
+
### Important
|
|
189
|
+
|
|
190
|
+
- **Don't try to handle everything yourself** - if there's likely a better specialist, consult first
|
|
191
|
+
- **Trust the orchestrator's decision** - it knows all available agents
|
|
192
|
+
- **If told to continue**, proceed confidently with the provided guidance
|
|
193
|
+
- **If routing happens**, your work is done - don't output anything more
|
|
194
|
+
|
|
195
|
+
### Example Usage
|
|
196
|
+
|
|
197
|
+
User says: "Now let's write a LinkedIn post about this blog"
|
|
198
|
+
|
|
199
|
+
You should call:
|
|
200
|
+
```
|
|
201
|
+
consult_orchestrator(
|
|
202
|
+
situation="Just finished writing a blog about AI agents for Product Managers",
|
|
203
|
+
user_request="Now let's write a LinkedIn post about it"
|
|
204
|
+
)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Then follow the orchestrator's decision.
|
|
208
|
+
'''
|
|
209
|
+
|
|
210
|
+
DEFAULT_AGENT_CONFIG = {
|
|
211
|
+
"name": DEFAULT_AGENT_NAME,
|
|
212
|
+
"description": "The default Sudosu assistant - a powerful all-in-one agent that can connect to Gmail, Calendar, GitHub, Linear, Slack and take actions across all your tools",
|
|
213
|
+
"model": "gemini-2.5-pro",
|
|
214
|
+
"tools": ["read_file", "write_file", "list_directory", "search_files", "run_command", "route_to_agent"],
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def format_agent_for_routing(agent: dict) -> str:
|
|
219
|
+
"""
|
|
220
|
+
Format agent info for the router's context with detailed capabilities.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
agent: Agent configuration dict
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Formatted string describing the agent
|
|
227
|
+
"""
|
|
228
|
+
name = agent.get('name', 'unknown')
|
|
229
|
+
description = agent.get('description', 'No description')
|
|
230
|
+
tools = agent.get('tools', [])
|
|
231
|
+
|
|
232
|
+
# Extract capabilities from tools
|
|
233
|
+
capabilities = []
|
|
234
|
+
if 'write_file' in tools:
|
|
235
|
+
capabilities.append("write/create files")
|
|
236
|
+
if 'read_file' in tools:
|
|
237
|
+
capabilities.append("read files")
|
|
238
|
+
if 'run_command' in tools:
|
|
239
|
+
capabilities.append("execute commands")
|
|
240
|
+
if 'list_directory' in tools:
|
|
241
|
+
capabilities.append("browse directories")
|
|
242
|
+
if 'search_files' in tools:
|
|
243
|
+
capabilities.append("search files")
|
|
244
|
+
|
|
245
|
+
# Get summary from system prompt (first meaningful lines)
|
|
246
|
+
system_prompt = agent.get('system_prompt', '')
|
|
247
|
+
summary_lines = []
|
|
248
|
+
for line in system_prompt.split('\n'):
|
|
249
|
+
line = line.strip()
|
|
250
|
+
if line and not line.startswith('#'):
|
|
251
|
+
summary_lines.append(line)
|
|
252
|
+
if len(summary_lines) >= 2:
|
|
253
|
+
break
|
|
254
|
+
summary = ' '.join(summary_lines)[:150]
|
|
255
|
+
if len(summary) == 150:
|
|
256
|
+
summary += '...'
|
|
257
|
+
|
|
258
|
+
capabilities_str = ', '.join(capabilities) if capabilities else 'basic'
|
|
259
|
+
|
|
260
|
+
result = f"### @{name}\n"
|
|
261
|
+
result += f"**Description**: {description}\n"
|
|
262
|
+
result += f"**Can**: {capabilities_str}\n"
|
|
263
|
+
if summary:
|
|
264
|
+
result += f"**Focus**: {summary}\n"
|
|
265
|
+
|
|
266
|
+
return result
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def format_user_context_for_prompt(profile: dict) -> str:
|
|
270
|
+
"""Format user profile for inclusion in system prompt.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
profile: User profile dictionary from onboarding
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Formatted string for system prompt, or empty string if no profile
|
|
277
|
+
"""
|
|
278
|
+
if not profile:
|
|
279
|
+
return ""
|
|
280
|
+
|
|
281
|
+
parts = ["## About the User\n"]
|
|
282
|
+
|
|
283
|
+
name = profile.get("name")
|
|
284
|
+
if name:
|
|
285
|
+
parts.append(f"- **Name**: {name} (use their name when appropriate)")
|
|
286
|
+
|
|
287
|
+
email = profile.get("email")
|
|
288
|
+
if email:
|
|
289
|
+
parts.append(f"- **Email**: {email}")
|
|
290
|
+
|
|
291
|
+
role = profile.get("role")
|
|
292
|
+
if role:
|
|
293
|
+
parts.append(f"- **Role**: {role}")
|
|
294
|
+
|
|
295
|
+
work = profile.get("work_context")
|
|
296
|
+
if work:
|
|
297
|
+
parts.append(f"- **Works on**: {work}")
|
|
298
|
+
|
|
299
|
+
goals = profile.get("goals")
|
|
300
|
+
if goals:
|
|
301
|
+
parts.append(f"- **Goals with Sudosu**: {goals}")
|
|
302
|
+
|
|
303
|
+
tools = profile.get("daily_tools", [])
|
|
304
|
+
if tools:
|
|
305
|
+
parts.append(f"- **Uses**: {', '.join(tools)}")
|
|
306
|
+
|
|
307
|
+
parts.append("\n**Personalization Guidelines**:")
|
|
308
|
+
parts.append("- Address the user by name occasionally (not every message)")
|
|
309
|
+
parts.append("- Tailor suggestions based on their role and goals")
|
|
310
|
+
parts.append("- Proactively suggest relevant integrations they use")
|
|
311
|
+
parts.append("- Keep communication style appropriate for their role")
|
|
312
|
+
|
|
313
|
+
return "\n".join(parts)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def get_default_agent_config(available_agents: list = None, cwd: str = "", user_profile: dict = None) -> dict:
|
|
317
|
+
"""
|
|
318
|
+
Get the default agent config with dynamic context.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
available_agents: List of available agent configurations
|
|
322
|
+
cwd: Current working directory
|
|
323
|
+
user_profile: User profile from onboarding (optional)
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Complete agent configuration dict
|
|
327
|
+
"""
|
|
328
|
+
config = DEFAULT_AGENT_CONFIG.copy()
|
|
329
|
+
|
|
330
|
+
# Format available agents with detailed info for routing
|
|
331
|
+
if available_agents:
|
|
332
|
+
agents_text = "\n".join([
|
|
333
|
+
format_agent_for_routing(a)
|
|
334
|
+
for a in available_agents
|
|
335
|
+
])
|
|
336
|
+
else:
|
|
337
|
+
agents_text = (
|
|
338
|
+
"*No agents created yet.*\n\n"
|
|
339
|
+
"When users need specialized help (writing, coding, etc.), "
|
|
340
|
+
"suggest creating an agent with `/agent create <name>`"
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Get project name from cwd
|
|
344
|
+
from pathlib import Path
|
|
345
|
+
project_name = Path(cwd).name if cwd else "Unknown"
|
|
346
|
+
|
|
347
|
+
# Format user context
|
|
348
|
+
user_context = format_user_context_for_prompt(user_profile)
|
|
349
|
+
|
|
350
|
+
# Build system prompt with dynamic context
|
|
351
|
+
base_prompt = DEFAULT_AGENT_SYSTEM_PROMPT.format(
|
|
352
|
+
available_agents=agents_text,
|
|
353
|
+
cwd=cwd,
|
|
354
|
+
project_name=project_name
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
# Insert user context after the first section if available
|
|
358
|
+
if user_context:
|
|
359
|
+
# Insert after "You are Sudosu" intro paragraph
|
|
360
|
+
insert_point = base_prompt.find("## Your Primary Role")
|
|
361
|
+
if insert_point > 0:
|
|
362
|
+
config["system_prompt"] = (
|
|
363
|
+
base_prompt[:insert_point] +
|
|
364
|
+
user_context + "\n\n" +
|
|
365
|
+
base_prompt[insert_point:]
|
|
366
|
+
)
|
|
367
|
+
else:
|
|
368
|
+
config["system_prompt"] = user_context + "\n\n" + base_prompt
|
|
369
|
+
else:
|
|
370
|
+
config["system_prompt"] = base_prompt
|
|
371
|
+
|
|
372
|
+
return config
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def generate_default_agent_md() -> str:
|
|
376
|
+
"""
|
|
377
|
+
Generate the content for the default AGENT.md file.
|
|
378
|
+
|
|
379
|
+
This file is created in .sudosu/AGENT.md when the user first runs sudosu
|
|
380
|
+
in a directory. Users can edit this file to customize their default agent.
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
Content for the AGENT.md file
|
|
384
|
+
"""
|
|
385
|
+
# The prompt includes placeholders that will be filled dynamically at runtime
|
|
386
|
+
prompt_content = '''# Sudosu - Your Powerful AI Assistant
|
|
387
|
+
|
|
388
|
+
You are Sudosu, the main AI assistant for this project. You are fully capable of handling ANY task.
|
|
389
|
+
|
|
390
|
+
## Your Primary Role
|
|
391
|
+
|
|
392
|
+
You have access to ALL tools and can do everything. However, follow this priority:
|
|
393
|
+
|
|
394
|
+
1. **FIRST: Check for specialists** - If a specialized agent exists that matches the task, route to them
|
|
395
|
+
2. **THEN: Handle it yourself** - If no specialist exists, YOU handle the task directly with your full capabilities
|
|
396
|
+
|
|
397
|
+
## 🔌 Cross-Tool Integration Superpowers
|
|
398
|
+
|
|
399
|
+
You can connect to and take ACTION across multiple tools - not just fetch data, but actually GET WORK DONE:
|
|
400
|
+
|
|
401
|
+
### Connected Tools & Actions:
|
|
402
|
+
- **Gmail**: Read emails, search inbox, compose & send messages, manage threads
|
|
403
|
+
- **Google Calendar**: Check availability, schedule meetings, create events, send invites
|
|
404
|
+
- **GitHub**: Create issues, review PRs, post comments, manage repositories, check notifications
|
|
405
|
+
- **Linear**: Track tasks, update issue status, prioritize tickets, create new issues
|
|
406
|
+
- **Slack**: Send messages, search conversations, post to channels, manage notifications
|
|
407
|
+
- and lot more
|
|
408
|
+
|
|
409
|
+
### Cross-Tool Workflows:
|
|
410
|
+
You can orchestrate complex tasks across multiple tools in a single conversation:
|
|
411
|
+
|
|
412
|
+
**Examples of what you can do:**
|
|
413
|
+
- "Check P0 issues in Linear, review their GitHub PRs, and email a status update to stakeholders"
|
|
414
|
+
- "Find next week's sprint meetings in Calendar, pull related Linear tickets, and create a sync doc"
|
|
415
|
+
- "Check my GitHub notifications, find PRs needing review, analyze the diffs, and post review comments"
|
|
416
|
+
- "Read my latest emails about the project, update the Linear ticket status, and send a Slack update to the team"
|
|
417
|
+
- "Schedule a meeting with the team in Calendar, create a Linear ticket for follow-up, and send an email with the agenda"
|
|
418
|
+
|
|
419
|
+
### Integration Guidelines:
|
|
420
|
+
1. **Ask for permission** before taking actions (sending emails, creating tickets, posting messages)
|
|
421
|
+
2. **Draft first, confirm second** - Show what you'll send/create before executing
|
|
422
|
+
3. **Explain your workflow** - Tell the user which tools you'll use and why
|
|
423
|
+
4. **Handle errors gracefully** - If a tool isn't connected, guide the user to connect it
|
|
424
|
+
5. **Be proactive** - Suggest cross-tool workflows when they'd save the user time
|
|
425
|
+
|
|
426
|
+
### Checking Tool Availability:
|
|
427
|
+
Before using integrations, you can reference `/integrations` to see what's connected.
|
|
428
|
+
If a tool isn't connected, guide the user: "Let's connect Gmail first with `/connect gmail`"
|
|
429
|
+
|
|
430
|
+
## Routing to Specialized Agents
|
|
431
|
+
|
|
432
|
+
### ALWAYS Route When:
|
|
433
|
+
- A specialized agent exists that clearly matches the user's task
|
|
434
|
+
- Examples:
|
|
435
|
+
- "Write a blog post" → route to blog-writer (if exists)
|
|
436
|
+
- "Create a LinkedIn post" → route to linkedin-writer (if exists)
|
|
437
|
+
- "Write a cold email" → route to cold-emailer-agent (if exists)
|
|
438
|
+
- "Help me with code" → route to coder agent (if exists)
|
|
439
|
+
|
|
440
|
+
### Handle Directly When:
|
|
441
|
+
- **No specialized agent exists** for the task - YOU do it yourself
|
|
442
|
+
- User is asking questions about the project
|
|
443
|
+
- User wants to know what agents are available
|
|
444
|
+
- User explicitly asks YOU (Sudosu) to handle it
|
|
445
|
+
- Simple file operations or project navigation
|
|
446
|
+
|
|
447
|
+
## How to Route
|
|
448
|
+
|
|
449
|
+
When routing, use the `route_to_agent` tool:
|
|
450
|
+
- `agent_name`: The exact name of the agent (e.g., "blog-writer")
|
|
451
|
+
- `message`: The user's original request, optionally refined with context
|
|
452
|
+
|
|
453
|
+
**IMPORTANT: Call `route_to_agent` only ONCE. After calling it, the routing is complete.
|
|
454
|
+
Do NOT call it multiple times. Simply confirm to the user that you're handing off to the agent and stop.**
|
|
455
|
+
|
|
456
|
+
## Your Full Capabilities
|
|
457
|
+
|
|
458
|
+
You have access to ALL tools and can:
|
|
459
|
+
- ✅ **Read files** to understand project context
|
|
460
|
+
- ✅ **Write and create files** - you CAN write files directly
|
|
461
|
+
- ✅ **List directories** to see project structure
|
|
462
|
+
- ✅ **Search for files** across the project
|
|
463
|
+
- ✅ **Execute shell commands** - you CAN run commands
|
|
464
|
+
- ✅ **Route tasks** to specialized agents
|
|
465
|
+
- ✅ **Connect to external tools** - Gmail, Calendar, GitHub, Linear, Slack
|
|
466
|
+
- ✅ **Take actions across tools** - send emails, schedule meetings, update tickets, post messages
|
|
467
|
+
- ✅ **Orchestrate workflows** - coordinate complex tasks across multiple tools
|
|
468
|
+
- ✅ **Answer questions** and provide guidance
|
|
469
|
+
|
|
470
|
+
**You are NOT limited.** If no specialist exists for a task, handle it yourself using your tools.
|
|
471
|
+
|
|
472
|
+
## Available Commands (for user reference)
|
|
473
|
+
|
|
474
|
+
- `/help` - Show all available commands
|
|
475
|
+
- `/agent` - List available agents
|
|
476
|
+
- `/agent create <name>` - Create a new agent
|
|
477
|
+
- `/config` - Show configuration
|
|
478
|
+
- `/quit` - Exit Sudosu
|
|
479
|
+
|
|
480
|
+
## Response Style
|
|
481
|
+
|
|
482
|
+
1. Be concise and helpful
|
|
483
|
+
2. When routing, explain the handoff briefly
|
|
484
|
+
3. Use markdown formatting
|
|
485
|
+
4. If unsure whether to route, ask the user for clarification
|
|
486
|
+
'''
|
|
487
|
+
|
|
488
|
+
return DEFAULT_AGENT_FRONTMATTER + prompt_content
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def load_default_agent_from_file(cwd: str = "") -> dict | None:
|
|
492
|
+
"""
|
|
493
|
+
Load the default agent configuration from .sudosu/AGENT.md if it exists.
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
cwd: Current working directory
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
Agent config dict if AGENT.md exists, None otherwise
|
|
500
|
+
"""
|
|
501
|
+
from pathlib import Path
|
|
502
|
+
import frontmatter
|
|
503
|
+
|
|
504
|
+
cwd_path = Path(cwd) if cwd else Path.cwd()
|
|
505
|
+
agent_file = cwd_path / ".sudosu" / "AGENT.md"
|
|
506
|
+
|
|
507
|
+
if not agent_file.exists():
|
|
508
|
+
return None
|
|
509
|
+
|
|
510
|
+
try:
|
|
511
|
+
with open(agent_file, "r", encoding="utf-8") as f:
|
|
512
|
+
post = frontmatter.load(f)
|
|
513
|
+
|
|
514
|
+
# Get description
|
|
515
|
+
description = post.get("description", "Your AI assistant")
|
|
516
|
+
if isinstance(description, list):
|
|
517
|
+
description = "\n".join(str(item) for item in description)
|
|
518
|
+
|
|
519
|
+
# Get tools list
|
|
520
|
+
tools = post.get("tools", ["read_file", "write_file", "list_directory", "search_files", "run_command", "route_to_agent"])
|
|
521
|
+
if isinstance(tools, str):
|
|
522
|
+
tools = [tools]
|
|
523
|
+
|
|
524
|
+
# Get integrations list
|
|
525
|
+
integrations = post.get("integrations", [])
|
|
526
|
+
if isinstance(integrations, str):
|
|
527
|
+
integrations = [integrations]
|
|
528
|
+
|
|
529
|
+
return {
|
|
530
|
+
"name": str(post.get("name", "sudosu")),
|
|
531
|
+
"description": str(description),
|
|
532
|
+
"model": str(post.get("model", "gemini-2.5-pro")),
|
|
533
|
+
"tools": tools,
|
|
534
|
+
"integrations": integrations,
|
|
535
|
+
"skills": post.get("skills", []),
|
|
536
|
+
"system_prompt": str(post.content).strip(),
|
|
537
|
+
"path": str(agent_file.parent),
|
|
538
|
+
}
|
|
539
|
+
except Exception as e:
|
|
540
|
+
print(f"Error loading AGENT.md: {e}")
|
|
541
|
+
return None
|
|
File without changes
|
sudosu/core/safety.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Safety checks for Sudosu operations."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Import brand colors - using hex values directly to avoid circular imports
|
|
7
|
+
COLOR_PRIMARY = "#FEEAC9"
|
|
8
|
+
COLOR_SECONDARY = "#FFCDC9"
|
|
9
|
+
COLOR_ACCENT = "#FD7979"
|
|
10
|
+
COLOR_INTERACTIVE = "#BDE3C3"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_safe_directory(cwd: Path = None) -> tuple[bool, str]:
|
|
14
|
+
"""
|
|
15
|
+
Check if the current directory is safe for Sudosu operations.
|
|
16
|
+
|
|
17
|
+
Sudosu should not run from:
|
|
18
|
+
- Home directory (~) - could expose sensitive dotfiles
|
|
19
|
+
- Root directory (/) - system-wide access
|
|
20
|
+
- System directories (/etc, /var, etc.)
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Tuple of (is_safe, reason_if_unsafe)
|
|
24
|
+
"""
|
|
25
|
+
cwd = cwd or Path.cwd()
|
|
26
|
+
home = Path.home()
|
|
27
|
+
|
|
28
|
+
# Block home directory
|
|
29
|
+
if cwd == home:
|
|
30
|
+
return False, "home directory (~)"
|
|
31
|
+
|
|
32
|
+
# Block root
|
|
33
|
+
if cwd == Path("/"):
|
|
34
|
+
return False, "root directory (/)"
|
|
35
|
+
|
|
36
|
+
# Block common system directories
|
|
37
|
+
unsafe_paths = ["/tmp", "/var", "/etc", "/usr", "/bin", "/sbin", "/opt"]
|
|
38
|
+
cwd_str = str(cwd)
|
|
39
|
+
for unsafe in unsafe_paths:
|
|
40
|
+
if cwd_str == unsafe or cwd_str.startswith(unsafe + "/"):
|
|
41
|
+
return False, f"system directory ({unsafe})"
|
|
42
|
+
|
|
43
|
+
return True, ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_safety_warning(reason: str) -> str:
|
|
47
|
+
"""Get a warning message for unsafe directories."""
|
|
48
|
+
return f"""
|
|
49
|
+
⚠️ [bold {COLOR_ACCENT}]Sudosu Safety Warning[/bold {COLOR_ACCENT}]
|
|
50
|
+
|
|
51
|
+
You are running Sudosu from your [bold]{reason}[/bold].
|
|
52
|
+
|
|
53
|
+
For security reasons, Sudosu agents can [bold {COLOR_ACCENT}]read and write files[/bold {COLOR_ACCENT}] in your
|
|
54
|
+
current directory. Running from this location could expose sensitive files
|
|
55
|
+
or cause unintended modifications.
|
|
56
|
+
|
|
57
|
+
[bold {COLOR_SECONDARY}]📁 Recommended Actions:[/bold {COLOR_SECONDARY}]
|
|
58
|
+
|
|
59
|
+
1. [{COLOR_INTERACTIVE}]Create a project folder:[/{COLOR_INTERACTIVE}]
|
|
60
|
+
[dim]mkdir ~/my-project && cd ~/my-project[/dim]
|
|
61
|
+
|
|
62
|
+
2. [{COLOR_INTERACTIVE}]Or navigate to an existing project:[/{COLOR_INTERACTIVE}]
|
|
63
|
+
[dim]cd ~/projects/my-app[/dim]
|
|
64
|
+
|
|
65
|
+
3. [{COLOR_INTERACTIVE}]Then run Sudosu:[/{COLOR_INTERACTIVE}]
|
|
66
|
+
[dim]sudosu[/dim]
|
|
67
|
+
|
|
68
|
+
[dim]Sudosu will only operate within the folder you start it from.[/dim]
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def is_home_directory(cwd: Path = None) -> bool:
|
|
73
|
+
"""Check if the current directory is the home directory."""
|
|
74
|
+
cwd = cwd or Path.cwd()
|
|
75
|
+
return cwd == Path.home()
|