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.
@@ -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()