supyagent 0.2.0__tar.gz → 0.2.2__tar.gz

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 supyagent might be problematic. Click here for more details.

Files changed (47) hide show
  1. {supyagent-0.2.0 → supyagent-0.2.2}/.gitignore +1 -0
  2. {supyagent-0.2.0 → supyagent-0.2.2}/PKG-INFO +27 -3
  3. {supyagent-0.2.0 → supyagent-0.2.2}/README.md +26 -2
  4. supyagent-0.2.2/agents/tester.yaml +23 -0
  5. {supyagent-0.2.0 → supyagent-0.2.2}/pyproject.toml +1 -1
  6. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/cli/main.py +83 -15
  7. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/llm.py +4 -0
  8. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/tools.py +43 -25
  9. supyagent-0.2.2/supyagent/default_tools/__init__.py +74 -0
  10. supyagent-0.2.2/supyagent/default_tools/files.py +439 -0
  11. supyagent-0.2.2/supyagent/default_tools/shell.py +217 -0
  12. supyagent-0.2.2/supypowers/__init__.py +1 -0
  13. supyagent-0.2.2/supypowers/files.py +439 -0
  14. supyagent-0.2.2/supypowers/shell.py +217 -0
  15. {supyagent-0.2.0 → supyagent-0.2.2}/LICENSE +0 -0
  16. {supyagent-0.2.0 → supyagent-0.2.2}/agents/assistant.yaml +0 -0
  17. {supyagent-0.2.0 → supyagent-0.2.2}/agents/coder.yaml +0 -0
  18. {supyagent-0.2.0 → supyagent-0.2.2}/agents/planner.yaml +0 -0
  19. {supyagent-0.2.0 → supyagent-0.2.2}/agents/researcher.yaml +0 -0
  20. {supyagent-0.2.0 → supyagent-0.2.2}/agents/summarizer.yaml +0 -0
  21. {supyagent-0.2.0 → supyagent-0.2.2}/agents/writer.yaml +0 -0
  22. {supyagent-0.2.0 → supyagent-0.2.2}/plans/initial_plan.md +0 -0
  23. {supyagent-0.2.0 → supyagent-0.2.2}/scripts/release.sh +0 -0
  24. {supyagent-0.2.0 → supyagent-0.2.2}/sprints/README.md +0 -0
  25. {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_1_foundation.md +0 -0
  26. {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_2_sessions.md +0 -0
  27. {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_3_repl.md +0 -0
  28. {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_4_execution.md +0 -0
  29. {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_5_multiagent.md +0 -0
  30. {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_6_polish.md +0 -0
  31. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/__init__.py +0 -0
  32. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/__main__.py +0 -0
  33. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/cli/__init__.py +0 -0
  34. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/__init__.py +0 -0
  35. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/agent.py +0 -0
  36. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/config.py +0 -0
  37. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/context.py +0 -0
  38. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/credentials.py +0 -0
  39. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/delegation.py +0 -0
  40. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/executor.py +0 -0
  41. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/registry.py +0 -0
  42. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/session_manager.py +0 -0
  43. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/models/__init__.py +0 -0
  44. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/models/agent_config.py +0 -0
  45. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/models/session.py +0 -0
  46. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/utils/__init__.py +0 -0
  47. {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/utils/paths.py +0 -0
@@ -38,6 +38,7 @@ ENV/
38
38
  # Environment variables
39
39
  .env
40
40
  .env.local
41
+ .env.supyagent
41
42
 
42
43
  # Test artifacts
43
44
  .pytest_cache/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supyagent
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: LLM agents powered by supypowers - build AI agents with tool use, multi-agent orchestration, and secure credential management
5
5
  Project-URL: Homepage, https://github.com/ergodic-ai/supyagent
6
6
  Project-URL: Documentation, https://github.com/ergodic-ai/supyagent#readme
@@ -68,6 +68,9 @@ uv pip install supyagent
68
68
  ## Quick Start
69
69
 
70
70
  ```bash
71
+ # Initialize supyagent (sets up default tools)
72
+ supyagent init
73
+
71
74
  # Set up your API key (stored securely)
72
75
  supyagent config set ANTHROPIC_API_KEY
73
76
 
@@ -229,6 +232,7 @@ delegates:
229
232
 
230
233
  | Command | Description |
231
234
  |---------|-------------|
235
+ | `supyagent init` | Initialize project with default tools |
232
236
  | `supyagent new <name>` | Create a new agent |
233
237
  | `supyagent list` | List all agents |
234
238
  | `supyagent show <name>` | Show agent details |
@@ -281,6 +285,25 @@ While chatting, use these commands:
281
285
  | `/clear` | Clear conversation history |
282
286
  | `/quit` | Exit the chat |
283
287
 
288
+ ## Bundled Tools
289
+
290
+ Running `supyagent init` installs these default tools:
291
+
292
+ ### Shell (`shell.py`)
293
+ - `run_command` - Execute shell commands
294
+ - `run_script` - Run multi-line bash scripts
295
+ - `which` - Find executable paths
296
+ - `get_env` - Get environment variables
297
+
298
+ ### Files (`files.py`)
299
+ - `read_file` / `write_file` - File I/O
300
+ - `list_directory` - List files with glob patterns
301
+ - `copy_file` / `move_file` / `delete_file` - File operations
302
+ - `create_directory` - Create directories
303
+ - `file_info` - Get file metadata
304
+
305
+ You can add your own tools by creating Python files in `supypowers/`.
306
+
284
307
  ## Project Structure
285
308
 
286
309
  ```
@@ -290,8 +313,9 @@ your-project/
290
313
  │ ├── planner.yaml
291
314
  │ └── researcher.yaml
292
315
  ├── supypowers/ # Tool definitions (Python)
293
- │ ├── hello.py
294
- └── web_search.py
316
+ │ ├── shell.py # Shell commands (bundled)
317
+ ├── files.py # File operations (bundled)
318
+ │ └── my_tools.py # Your custom tools
295
319
  └── .supyagent/ # Runtime data (gitignore this)
296
320
  ├── sessions/ # Conversation history
297
321
  ├── credentials/ # Encrypted secrets
@@ -32,6 +32,9 @@ uv pip install supyagent
32
32
  ## Quick Start
33
33
 
34
34
  ```bash
35
+ # Initialize supyagent (sets up default tools)
36
+ supyagent init
37
+
35
38
  # Set up your API key (stored securely)
36
39
  supyagent config set ANTHROPIC_API_KEY
37
40
 
@@ -193,6 +196,7 @@ delegates:
193
196
 
194
197
  | Command | Description |
195
198
  |---------|-------------|
199
+ | `supyagent init` | Initialize project with default tools |
196
200
  | `supyagent new <name>` | Create a new agent |
197
201
  | `supyagent list` | List all agents |
198
202
  | `supyagent show <name>` | Show agent details |
@@ -245,6 +249,25 @@ While chatting, use these commands:
245
249
  | `/clear` | Clear conversation history |
246
250
  | `/quit` | Exit the chat |
247
251
 
252
+ ## Bundled Tools
253
+
254
+ Running `supyagent init` installs these default tools:
255
+
256
+ ### Shell (`shell.py`)
257
+ - `run_command` - Execute shell commands
258
+ - `run_script` - Run multi-line bash scripts
259
+ - `which` - Find executable paths
260
+ - `get_env` - Get environment variables
261
+
262
+ ### Files (`files.py`)
263
+ - `read_file` / `write_file` - File I/O
264
+ - `list_directory` - List files with glob patterns
265
+ - `copy_file` / `move_file` / `delete_file` - File operations
266
+ - `create_directory` - Create directories
267
+ - `file_info` - Get file metadata
268
+
269
+ You can add your own tools by creating Python files in `supypowers/`.
270
+
248
271
  ## Project Structure
249
272
 
250
273
  ```
@@ -254,8 +277,9 @@ your-project/
254
277
  │ ├── planner.yaml
255
278
  │ └── researcher.yaml
256
279
  ├── supypowers/ # Tool definitions (Python)
257
- │ ├── hello.py
258
- └── web_search.py
280
+ │ ├── shell.py # Shell commands (bundled)
281
+ ├── files.py # File operations (bundled)
282
+ │ └── my_tools.py # Your custom tools
259
283
  └── .supyagent/ # Runtime data (gitignore this)
260
284
  ├── sessions/ # Conversation history
261
285
  ├── credentials/ # Encrypted secrets
@@ -0,0 +1,23 @@
1
+ name: tester
2
+ description: A test agent to verify supyagent works
3
+ version: "1.0"
4
+ type: interactive
5
+
6
+ model:
7
+ provider: openrouter/moonshotai/kimi-k2.5
8
+ temperature: 0.7
9
+ max_tokens: 4096
10
+
11
+ system_prompt: |
12
+ You are a helpful AI assistant for testing supyagent.
13
+ You have access to shell commands and file operations.
14
+
15
+ When asked to do something, use the available tools.
16
+ Be concise in your responses.
17
+
18
+ tools:
19
+ allow:
20
+ - "*"
21
+
22
+ limits:
23
+ max_tool_calls_per_turn: 10
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "supyagent"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "LLM agents powered by supypowers - build AI agents with tool use, multi-agent orchestration, and secure credential management"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -21,13 +21,15 @@ from supyagent.core.config import ConfigManager, load_config
21
21
  from supyagent.core.executor import ExecutionRunner
22
22
  from supyagent.core.registry import AgentRegistry
23
23
  from supyagent.core.session_manager import SessionManager
24
+ from supyagent.default_tools import install_default_tools, list_default_tools
24
25
  from supyagent.models.agent_config import AgentNotFoundError, load_agent_config
25
26
 
26
27
  console = Console()
28
+ console_err = Console(stderr=True)
27
29
 
28
30
 
29
31
  @click.group()
30
- @click.version_option(version="0.2.0", prog_name="supyagent")
32
+ @click.version_option(version="0.2.2", prog_name="supyagent")
31
33
  def cli():
32
34
  """
33
35
  Supyagent - LLM agents powered by supypowers.
@@ -37,12 +39,82 @@ def cli():
37
39
  Quick start:
38
40
 
39
41
  \b
42
+ supyagent init # Set up default tools
43
+ supyagent config set # Configure API keys
40
44
  supyagent new myagent # Create an agent
41
45
  supyagent chat myagent # Start chatting
42
46
  """
43
47
  pass
44
48
 
45
49
 
50
+ @cli.command()
51
+ @click.option(
52
+ "--tools-dir",
53
+ "-t",
54
+ default="supypowers",
55
+ help="Directory for tools (default: supypowers/)",
56
+ )
57
+ @click.option(
58
+ "--force",
59
+ "-f",
60
+ is_flag=True,
61
+ help="Overwrite existing files",
62
+ )
63
+ def init(tools_dir: str, force: bool):
64
+ """
65
+ Initialize supyagent in the current directory.
66
+
67
+ This sets up:
68
+ - Default tools in supypowers/ (shell commands, file operations)
69
+ - agents/ directory for agent configurations
70
+
71
+ \b
72
+ Examples:
73
+ supyagent init
74
+ supyagent init --tools-dir my_tools
75
+ """
76
+ console.print("[bold]Initializing supyagent...[/bold]")
77
+ console.print()
78
+
79
+ # Create agents directory
80
+ agents_dir = Path("agents")
81
+ if not agents_dir.exists():
82
+ agents_dir.mkdir(parents=True)
83
+ console.print(f" [green]✓[/green] Created {agents_dir}/")
84
+ else:
85
+ console.print(f" [dim]○[/dim] {agents_dir}/ already exists")
86
+
87
+ # Install default tools
88
+ tools_path = Path(tools_dir)
89
+
90
+ if force:
91
+ # Remove and reinstall
92
+ import shutil
93
+
94
+ if tools_path.exists():
95
+ shutil.rmtree(tools_path)
96
+
97
+ if tools_path.exists() and any(tools_path.glob("*.py")):
98
+ console.print(f" [dim]○[/dim] {tools_dir}/ already has tools")
99
+ else:
100
+ count = install_default_tools(tools_path)
101
+ console.print(
102
+ f" [green]✓[/green] Installed {count} default tools to {tools_dir}/"
103
+ )
104
+
105
+ # Show available tools
106
+ console.print()
107
+ console.print("[bold]Available tools:[/bold]")
108
+ for tool in list_default_tools():
109
+ console.print(f" • [cyan]{tool['name']}[/cyan]: {tool['description']}")
110
+
111
+ console.print()
112
+ console.print("[bold]Next steps:[/bold]")
113
+ console.print(" 1. Configure your API key: [cyan]supyagent config set[/cyan]")
114
+ console.print(" 2. Create an agent: [cyan]supyagent new myagent[/cyan]")
115
+ console.print(" 3. Start chatting: [cyan]supyagent chat myagent[/cyan]")
116
+
117
+
46
118
  @cli.command()
47
119
  @click.argument("name")
48
120
  @click.option(
@@ -636,15 +708,14 @@ def run(
636
708
  try:
637
709
  config = load_agent_config(agent_name)
638
710
  except AgentNotFoundError as e:
639
- console.print(f"[red]Error:[/red] {e}", err=True)
711
+ console_err.print(f"[red]Error:[/red] {e}")
640
712
  sys.exit(1)
641
713
 
642
714
  # Warn if using interactive agent in execution mode
643
715
  if config.type != "execution" and not quiet:
644
- console.print(
716
+ console_err.print(
645
717
  f"[yellow]Note:[/yellow] '{agent_name}' is an interactive agent. "
646
- "Consider using 'chat' for interactive use.",
647
- err=True,
718
+ "Consider using 'chat' for interactive use."
648
719
  )
649
720
 
650
721
  # Parse secrets
@@ -659,9 +730,7 @@ def run(
659
730
  else:
660
731
  input_path = Path(input_file)
661
732
  if not input_path.exists():
662
- console.print(
663
- f"[red]Error:[/red] File not found: {input_file}", err=True
664
- )
733
+ console_err.print(f"[red]Error:[/red] File not found: {input_file}")
665
734
  sys.exit(1)
666
735
  task_content = input_path.read_text().strip()
667
736
  elif task:
@@ -675,22 +744,21 @@ def run(
675
744
  if not sys.stdin.isatty():
676
745
  task_content = sys.stdin.read().strip()
677
746
  else:
678
- console.print(
747
+ console_err.print(
679
748
  "[red]Error:[/red] No task provided. "
680
- "Use positional argument, --input, or pipe to stdin.",
681
- err=True,
749
+ "Use positional argument, --input, or pipe to stdin."
682
750
  )
683
751
  sys.exit(1)
684
752
 
685
753
  if not task_content:
686
- console.print("[red]Error:[/red] Empty task", err=True)
754
+ console_err.print("[red]Error:[/red] Empty task")
687
755
  sys.exit(1)
688
756
 
689
757
  # Run the agent
690
758
  runner = ExecutionRunner(config)
691
759
 
692
760
  if not quiet:
693
- console.print(f"[dim]Running {agent_name}...[/dim]", err=True)
761
+ console_err.print(f"[dim]Running {agent_name}...[/dim]")
694
762
 
695
763
  result = runner.run(task_content, secrets=secrets_dict, output_format=output_format)
696
764
 
@@ -700,7 +768,7 @@ def run(
700
768
  elif result["ok"]:
701
769
  click.echo(result["data"])
702
770
  else:
703
- console.print(f"[red]Error:[/red] {result['error']}", err=True)
771
+ console_err.print(f"[red]Error:[/red] {result['error']}")
704
772
  sys.exit(1)
705
773
 
706
774
 
@@ -756,7 +824,7 @@ def batch(
756
824
  try:
757
825
  config = load_agent_config(agent_name)
758
826
  except AgentNotFoundError as e:
759
- console.print(f"[red]Error:[/red] {e}", err=True)
827
+ console_err.print(f"[red]Error:[/red] {e}")
760
828
  sys.exit(1)
761
829
 
762
830
  # Parse secrets
@@ -4,9 +4,13 @@ LiteLLM wrapper for unified LLM access.
4
4
 
5
5
  from typing import Any
6
6
 
7
+ import litellm
7
8
  from litellm import completion
8
9
  from litellm.types.utils import ModelResponse
9
10
 
11
+ # Suppress LiteLLM debug messages (e.g., "Provider List: ...")
12
+ litellm.suppress_debug_info = True
13
+
10
14
 
11
15
  class LLMClient:
12
16
  """
@@ -193,43 +193,61 @@ def supypowers_to_openai_tools(sp_tools: list[dict[str, Any]]) -> list[dict[str,
193
193
  Convert supypowers tool definitions to OpenAI function calling format.
194
194
 
195
195
  Supypowers docs output format:
196
- {
197
- "script": "hello",
198
- "function": "hello",
199
- "description": "...",
200
- "input_schema": {...}
201
- }
196
+ [
197
+ {
198
+ "script": "/path/to/script.py",
199
+ "functions": [
200
+ {
201
+ "name": "function_name",
202
+ "description": "...",
203
+ "input_schema": {...}
204
+ }
205
+ ]
206
+ }
207
+ ]
202
208
 
203
209
  OpenAI format:
204
210
  {
205
211
  "type": "function",
206
212
  "function": {
207
- "name": "hello__hello",
213
+ "name": "script__function_name",
208
214
  "description": "...",
209
215
  "parameters": {...}
210
216
  }
211
217
  }
212
218
  """
219
+ import os
220
+
213
221
  openai_tools = []
214
222
 
215
- for sp_tool in sp_tools:
216
- script = sp_tool.get("script", "")
217
- func = sp_tool.get("function", "")
218
- description = sp_tool.get("description", "No description")
219
- input_schema = sp_tool.get("input_schema", {"type": "object", "properties": {}})
220
-
221
- # Use double underscore to join script:function (since : isn't allowed in function names)
222
- name = f"{script}__{func}"
223
-
224
- openai_tool = {
225
- "type": "function",
226
- "function": {
227
- "name": name,
228
- "description": description,
229
- "parameters": input_schema,
230
- },
231
- }
223
+ for script_entry in sp_tools:
224
+ script_path = script_entry.get("script", "")
225
+ functions = script_entry.get("functions", [])
226
+
227
+ # Extract script name from path (e.g., "files" from "/path/to/files.py")
228
+ script_name = os.path.splitext(os.path.basename(script_path))[0]
229
+
230
+ # Skip __init__ files with no functions
231
+ if script_name == "__init__" and not functions:
232
+ continue
233
+
234
+ for func_def in functions:
235
+ func_name = func_def.get("name", "")
236
+ description = func_def.get("description", "No description")
237
+ input_schema = func_def.get("input_schema", {"type": "object", "properties": {}})
238
+
239
+ # Use double underscore to join script:function (since : isn't allowed in function names)
240
+ name = f"{script_name}__{func_name}"
241
+
242
+ openai_tool = {
243
+ "type": "function",
244
+ "function": {
245
+ "name": name,
246
+ "description": description,
247
+ "parameters": input_schema,
248
+ },
249
+ }
232
250
 
233
- openai_tools.append(openai_tool)
251
+ openai_tools.append(openai_tool)
234
252
 
235
253
  return openai_tools
@@ -0,0 +1,74 @@
1
+ """
2
+ Default supypowers tools bundled with supyagent.
3
+
4
+ These tools are copied to the user's project when running `supyagent init`.
5
+ """
6
+
7
+ import shutil
8
+ from pathlib import Path
9
+
10
+ # Path to the bundled default tools
11
+ TOOLS_DIR = Path(__file__).parent
12
+
13
+
14
+ def get_bundled_tools() -> list[Path]:
15
+ """Get list of bundled tool files."""
16
+ return [f for f in TOOLS_DIR.glob("*.py") if f.name != "__init__.py"]
17
+
18
+
19
+ def install_default_tools(target_dir: Path | str = "supypowers") -> int:
20
+ """
21
+ Install default tools to a target directory.
22
+
23
+ Args:
24
+ target_dir: Directory to install tools to (default: supypowers/)
25
+
26
+ Returns:
27
+ Number of files installed
28
+ """
29
+ target = Path(target_dir)
30
+ target.mkdir(parents=True, exist_ok=True)
31
+
32
+ installed = 0
33
+ for tool_file in get_bundled_tools():
34
+ dest = target / tool_file.name
35
+ if not dest.exists():
36
+ shutil.copy(tool_file, dest)
37
+ installed += 1
38
+
39
+ # Create __init__.py if not exists
40
+ init_file = target / "__init__.py"
41
+ if not init_file.exists():
42
+ init_file.write_text('"""Supypowers tools for this project."""\n')
43
+ installed += 1
44
+
45
+ return installed
46
+
47
+
48
+ def list_default_tools() -> list[dict]:
49
+ """
50
+ List available default tools.
51
+
52
+ Returns:
53
+ List of tool info dicts
54
+ """
55
+ tools = []
56
+ for tool_file in get_bundled_tools():
57
+ # Read first docstring
58
+ content = tool_file.read_text()
59
+ description = ""
60
+ if '"""' in content:
61
+ start = content.find('"""') + 3
62
+ end = content.find('"""', start)
63
+ if end > start:
64
+ description = content[start:end].strip().split("\n")[0]
65
+
66
+ tools.append(
67
+ {
68
+ "name": tool_file.stem,
69
+ "file": tool_file.name,
70
+ "description": description,
71
+ }
72
+ )
73
+
74
+ return tools