agent-shell-py 0.1.0__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.
- agent_shell_py-0.1.0/PKG-INFO +62 -0
- agent_shell_py-0.1.0/README.md +55 -0
- agent_shell_py-0.1.0/pyproject.toml +21 -0
- agent_shell_py-0.1.0/setup.cfg +4 -0
- agent_shell_py-0.1.0/src/agent_shell/__init__.py +0 -0
- agent_shell_py-0.1.0/src/agent_shell/adapters/__init__.py +0 -0
- agent_shell_py-0.1.0/src/agent_shell/adapters/agent_adapter_protocol.py +28 -0
- agent_shell_py-0.1.0/src/agent_shell/adapters/claude_code_adapter.py +140 -0
- agent_shell_py-0.1.0/src/agent_shell/models/__init__.py +0 -0
- agent_shell_py-0.1.0/src/agent_shell/models/agent.py +22 -0
- agent_shell_py-0.1.0/src/agent_shell/shell.py +77 -0
- agent_shell_py-0.1.0/src/agent_shell_py.egg-info/PKG-INFO +62 -0
- agent_shell_py-0.1.0/src/agent_shell_py.egg-info/SOURCES.txt +13 -0
- agent_shell_py-0.1.0/src/agent_shell_py.egg-info/dependency_links.txt +1 -0
- agent_shell_py-0.1.0/src/agent_shell_py.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-shell-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight abstraction for executing CLI coding agents headlessly
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# Agent Shell
|
|
9
|
+
Agent Shell is a light weight abstraction for executing a cli coding agent headlessly
|
|
10
|
+
and returning the output that can be used programatically as a unified contract
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Execute
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from agent_shell.shell import AgentShell
|
|
18
|
+
from agent_shell.models.agent import AgentType
|
|
19
|
+
|
|
20
|
+
shell = AgentShell(agent_type=AgentType.CLAUDE_CODE)
|
|
21
|
+
|
|
22
|
+
response = await shell.execute(
|
|
23
|
+
cwd="/path/to/project",
|
|
24
|
+
prompt="Can you tell me about this project?",
|
|
25
|
+
allowed_tools=["Read", "Glob", "Grep"],
|
|
26
|
+
model="sonnet",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
print(response.response)
|
|
30
|
+
print(f"Cost: ${response.cost:.4f}")
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Stream
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from agent_shell.shell import AgentShell
|
|
37
|
+
from agent_shell.models.agent import AgentType
|
|
38
|
+
|
|
39
|
+
shell = AgentShell(agent_type=AgentType.CLAUDE_CODE)
|
|
40
|
+
|
|
41
|
+
async for event in shell.stream(
|
|
42
|
+
cwd="/path/to/project",
|
|
43
|
+
prompt="Refactor the auth module",
|
|
44
|
+
allowed_tools=["Read", "Edit", "Bash"],
|
|
45
|
+
model="sonnet",
|
|
46
|
+
effort="high",
|
|
47
|
+
include_thinking=True,
|
|
48
|
+
):
|
|
49
|
+
print(f"[{event.type}] {event.content}")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Supported CLI Agents:
|
|
53
|
+
|
|
54
|
+
- [x] Claude Code
|
|
55
|
+
- [ ] OpenCode
|
|
56
|
+
- [ ] Gemini CLI
|
|
57
|
+
- [ ] Copilot CLI
|
|
58
|
+
- [ ] Codex
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Agent Shell
|
|
2
|
+
Agent Shell is a light weight abstraction for executing a cli coding agent headlessly
|
|
3
|
+
and returning the output that can be used programatically as a unified contract
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
### Execute
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from agent_shell.shell import AgentShell
|
|
11
|
+
from agent_shell.models.agent import AgentType
|
|
12
|
+
|
|
13
|
+
shell = AgentShell(agent_type=AgentType.CLAUDE_CODE)
|
|
14
|
+
|
|
15
|
+
response = await shell.execute(
|
|
16
|
+
cwd="/path/to/project",
|
|
17
|
+
prompt="Can you tell me about this project?",
|
|
18
|
+
allowed_tools=["Read", "Glob", "Grep"],
|
|
19
|
+
model="sonnet",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
print(response.response)
|
|
23
|
+
print(f"Cost: ${response.cost:.4f}")
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Stream
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from agent_shell.shell import AgentShell
|
|
30
|
+
from agent_shell.models.agent import AgentType
|
|
31
|
+
|
|
32
|
+
shell = AgentShell(agent_type=AgentType.CLAUDE_CODE)
|
|
33
|
+
|
|
34
|
+
async for event in shell.stream(
|
|
35
|
+
cwd="/path/to/project",
|
|
36
|
+
prompt="Refactor the auth module",
|
|
37
|
+
allowed_tools=["Read", "Edit", "Bash"],
|
|
38
|
+
model="sonnet",
|
|
39
|
+
effort="high",
|
|
40
|
+
include_thinking=True,
|
|
41
|
+
):
|
|
42
|
+
print(f"[{event.type}] {event.content}")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Supported CLI Agents:
|
|
46
|
+
|
|
47
|
+
- [x] Claude Code
|
|
48
|
+
- [ ] OpenCode
|
|
49
|
+
- [ ] Gemini CLI
|
|
50
|
+
- [ ] Copilot CLI
|
|
51
|
+
- [ ] Codex
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "agent-shell-py"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A lightweight abstraction for executing CLI coding agents headlessly"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = []
|
|
8
|
+
[tool.setuptools.packages.find]
|
|
9
|
+
where = ["src"]
|
|
10
|
+
|
|
11
|
+
[tool.pytest.ini_options]
|
|
12
|
+
asyncio_mode = "auto"
|
|
13
|
+
markers = [
|
|
14
|
+
"e2e: end-to-end tests that call real CLI agents (requires credentials, costs money)",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[dependency-groups]
|
|
18
|
+
dev = [
|
|
19
|
+
"pytest>=9.0.2",
|
|
20
|
+
"pytest-asyncio>=1.3.0",
|
|
21
|
+
]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import Protocol, AsyncIterator
|
|
2
|
+
from agent_shell.models.agent import AgentResponse, StreamEvent
|
|
3
|
+
|
|
4
|
+
class AgentAdapter(Protocol):
|
|
5
|
+
async def execute(
|
|
6
|
+
self,
|
|
7
|
+
cwd: str,
|
|
8
|
+
prompt: str,
|
|
9
|
+
allowed_tools: list[str] | None = None,
|
|
10
|
+
model: str | None = None,
|
|
11
|
+
effort: str | None = None,
|
|
12
|
+
include_thinking: bool = False,
|
|
13
|
+
) -> AgentResponse:
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
def stream(
|
|
17
|
+
self,
|
|
18
|
+
cwd: str,
|
|
19
|
+
prompt: str,
|
|
20
|
+
allowed_tools: list[str] | None = None,
|
|
21
|
+
model: str | None = None,
|
|
22
|
+
effort: str | None = None,
|
|
23
|
+
include_thinking: bool = False,
|
|
24
|
+
) -> AsyncIterator[StreamEvent]:
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
async def cancel(self) -> None:
|
|
28
|
+
...
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from typing import AsyncIterator
|
|
5
|
+
|
|
6
|
+
from agent_shell.models.agent import AgentResponse, StreamEvent
|
|
7
|
+
|
|
8
|
+
class ClaudeCodeAdapter():
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self._active_processes = []
|
|
11
|
+
|
|
12
|
+
async def execute(
|
|
13
|
+
self,
|
|
14
|
+
cwd: str,
|
|
15
|
+
prompt: str,
|
|
16
|
+
allowed_tools: list[str] | None = None,
|
|
17
|
+
model: str | None = None,
|
|
18
|
+
effort: str | None = None,
|
|
19
|
+
include_thinking: bool = False,
|
|
20
|
+
) -> AgentResponse:
|
|
21
|
+
chunks: list[StreamEvent] = []
|
|
22
|
+
async for event in self.stream(
|
|
23
|
+
cwd=cwd,
|
|
24
|
+
prompt=prompt,
|
|
25
|
+
allowed_tools=allowed_tools,
|
|
26
|
+
model=model,
|
|
27
|
+
effort=effort,
|
|
28
|
+
include_thinking=include_thinking,
|
|
29
|
+
):
|
|
30
|
+
chunks.append(event)
|
|
31
|
+
|
|
32
|
+
text = "\n".join(e.content for e in chunks if e.type == "text")
|
|
33
|
+
cost = next((e.cost for e in reversed(chunks) if e.type == "result"), 0.0)
|
|
34
|
+
return AgentResponse(response=text, cost=cost)
|
|
35
|
+
|
|
36
|
+
async def stream(
|
|
37
|
+
self,
|
|
38
|
+
cwd: str,
|
|
39
|
+
prompt: str,
|
|
40
|
+
allowed_tools: list[str] | None = None,
|
|
41
|
+
model: str | None = None,
|
|
42
|
+
effort: str | None = None,
|
|
43
|
+
include_thinking: bool = False,
|
|
44
|
+
) -> AsyncIterator[StreamEvent]:
|
|
45
|
+
cmd = [
|
|
46
|
+
"claude", "-p", prompt,
|
|
47
|
+
"--output-format", "stream-json",
|
|
48
|
+
"--verbose"
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
if allowed_tools:
|
|
52
|
+
cmd.extend(["--allowed-tools", ",".join(allowed_tools)])
|
|
53
|
+
|
|
54
|
+
if model:
|
|
55
|
+
cmd.extend(["--model", model])
|
|
56
|
+
|
|
57
|
+
if effort:
|
|
58
|
+
cmd.extend(["--effort", effort])
|
|
59
|
+
|
|
60
|
+
process = await asyncio.create_subprocess_exec(
|
|
61
|
+
*cmd,
|
|
62
|
+
stdin=asyncio.subprocess.DEVNULL,
|
|
63
|
+
stdout=asyncio.subprocess.PIPE,
|
|
64
|
+
stderr=asyncio.subprocess.PIPE,
|
|
65
|
+
cwd=os.path.abspath(cwd),
|
|
66
|
+
preexec_fn=os.setsid,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
self._active_processes.append(process)
|
|
70
|
+
|
|
71
|
+
buffer = ""
|
|
72
|
+
while True:
|
|
73
|
+
chunk = await process.stdout.read(65536)
|
|
74
|
+
if not chunk:
|
|
75
|
+
if buffer.strip():
|
|
76
|
+
try:
|
|
77
|
+
for event in self._parse_event(
|
|
78
|
+
event=json.loads(buffer),
|
|
79
|
+
include_thinking=include_thinking
|
|
80
|
+
):
|
|
81
|
+
yield event
|
|
82
|
+
except json.JSONDecodeError:
|
|
83
|
+
pass
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
buffer += chunk.decode("utf-8")
|
|
87
|
+
while "\n" in buffer:
|
|
88
|
+
line, buffer = buffer.split("\n", 1)
|
|
89
|
+
if line.strip():
|
|
90
|
+
try:
|
|
91
|
+
for event in self._parse_event(
|
|
92
|
+
event=json.loads(line),
|
|
93
|
+
include_thinking=include_thinking):
|
|
94
|
+
yield event
|
|
95
|
+
except json.JSONDecodeError:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
await process.wait()
|
|
99
|
+
if process in self._active_processes:
|
|
100
|
+
self._active_processes.remove(process)
|
|
101
|
+
|
|
102
|
+
stderr = await process.stderr.read()
|
|
103
|
+
if stderr and process.returncode != 0:
|
|
104
|
+
yield StreamEvent(type="error", content=stderr.decode("utf-8")[-500:])
|
|
105
|
+
|
|
106
|
+
def _parse_event(self, event: dict, include_thinking: bool) -> list[StreamEvent]:
|
|
107
|
+
t = event.get("type", "")
|
|
108
|
+
events = []
|
|
109
|
+
|
|
110
|
+
if t == "assistant":
|
|
111
|
+
for item in event.get("message", {}).get("content", []):
|
|
112
|
+
if item.get("type") == "text":
|
|
113
|
+
events.append(StreamEvent(type="text", content=item["text"]))
|
|
114
|
+
elif item.get("type") == "tool_use":
|
|
115
|
+
events.append(StreamEvent(type="tool_use", content=item.get("name", "")))
|
|
116
|
+
elif item.get("type") == "thinking" and include_thinking:
|
|
117
|
+
events.append(StreamEvent(type="thinking", content=item.get("thinking", "")))
|
|
118
|
+
|
|
119
|
+
elif t == "result":
|
|
120
|
+
cost = event.get("total_cost_usd", 0) or 0
|
|
121
|
+
duration = (event.get("duration_ms", 0) or 0) / 1000
|
|
122
|
+
is_error = event.get("is_error", False)
|
|
123
|
+
status = "error" if is_error else "ok"
|
|
124
|
+
events.append(StreamEvent(type="result", content=status, cost=cost, duration=duration))
|
|
125
|
+
|
|
126
|
+
return events
|
|
127
|
+
|
|
128
|
+
async def cancel(self) -> None:
|
|
129
|
+
for process in self._active_processes:
|
|
130
|
+
try:
|
|
131
|
+
os.killpg(os.getpgid(process.pid), 9)
|
|
132
|
+
except ProcessLookupError:
|
|
133
|
+
pass
|
|
134
|
+
self._active_processes.clear()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
class AgentType(StrEnum):
|
|
5
|
+
CLAUDE_CODE = "claude_code"
|
|
6
|
+
OPENCODE = "opencode"
|
|
7
|
+
GEMINI_CLI = "gemini_cli"
|
|
8
|
+
COPILOT_CLI = "copilot_cli"
|
|
9
|
+
CODEX = "codex"
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class AgentResponse:
|
|
13
|
+
response: str
|
|
14
|
+
cost: float
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class StreamEvent:
|
|
18
|
+
type: str
|
|
19
|
+
content: str
|
|
20
|
+
cost: float = 0.0
|
|
21
|
+
duration: float = 0.0
|
|
22
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import AsyncIterator
|
|
3
|
+
|
|
4
|
+
from agent_shell.models.agent import AgentType, AgentResponse, StreamEvent
|
|
5
|
+
from agent_shell.adapters.agent_adapter_protocol import AgentAdapter
|
|
6
|
+
from agent_shell.adapters.claude_code_adapter import ClaudeCodeAdapter
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentShell():
|
|
10
|
+
def __init__(self, agent_type: AgentType):
|
|
11
|
+
self._adapter = self._resolve_adapter(agent_type=agent_type)
|
|
12
|
+
|
|
13
|
+
def _resolve_adapter(self, agent_type: AgentType) -> AgentAdapter:
|
|
14
|
+
adapters = {
|
|
15
|
+
AgentType.CLAUDE_CODE: ClaudeCodeAdapter
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
adapter_cls = adapters.get(agent_type)
|
|
19
|
+
|
|
20
|
+
if not adapter_cls:
|
|
21
|
+
raise ValueError(f"Unsupported agent: {agent_type}")
|
|
22
|
+
|
|
23
|
+
return adapter_cls()
|
|
24
|
+
|
|
25
|
+
async def execute(
|
|
26
|
+
self,
|
|
27
|
+
cwd: str,
|
|
28
|
+
prompt: str,
|
|
29
|
+
allowed_tools: list[str] | None = None,
|
|
30
|
+
model: str | None = None,
|
|
31
|
+
effort: str | None = None,
|
|
32
|
+
include_thinking: bool = False,
|
|
33
|
+
) -> AgentResponse:
|
|
34
|
+
|
|
35
|
+
if not Path(cwd).is_dir():
|
|
36
|
+
raise ValueError(f"Directory does not exist: {cwd}")
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
return await self._adapter.execute(
|
|
40
|
+
cwd=cwd,
|
|
41
|
+
prompt=prompt,
|
|
42
|
+
allowed_tools=allowed_tools,
|
|
43
|
+
model=model,
|
|
44
|
+
effort=effort,
|
|
45
|
+
include_thinking=include_thinking,
|
|
46
|
+
)
|
|
47
|
+
except KeyboardInterrupt:
|
|
48
|
+
await self._adapter.cancel()
|
|
49
|
+
raise
|
|
50
|
+
|
|
51
|
+
async def stream(
|
|
52
|
+
self,
|
|
53
|
+
cwd: str,
|
|
54
|
+
prompt: str,
|
|
55
|
+
allowed_tools: list[str] | None = None,
|
|
56
|
+
model: str | None = None,
|
|
57
|
+
effort: str | None = None,
|
|
58
|
+
include_thinking: bool = False,
|
|
59
|
+
) -> AsyncIterator[StreamEvent]:
|
|
60
|
+
|
|
61
|
+
if not Path(cwd).is_dir():
|
|
62
|
+
raise ValueError(f"Directory does not exist: {cwd}")
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
async for chunk in self._adapter.stream(
|
|
66
|
+
cwd=cwd,
|
|
67
|
+
prompt=prompt,
|
|
68
|
+
allowed_tools=allowed_tools,
|
|
69
|
+
model=model,
|
|
70
|
+
effort=effort,
|
|
71
|
+
include_thinking=include_thinking,
|
|
72
|
+
):
|
|
73
|
+
yield chunk
|
|
74
|
+
except KeyboardInterrupt:
|
|
75
|
+
await self._adapter.cancel()
|
|
76
|
+
raise
|
|
77
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-shell-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight abstraction for executing CLI coding agents headlessly
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# Agent Shell
|
|
9
|
+
Agent Shell is a light weight abstraction for executing a cli coding agent headlessly
|
|
10
|
+
and returning the output that can be used programatically as a unified contract
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Execute
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from agent_shell.shell import AgentShell
|
|
18
|
+
from agent_shell.models.agent import AgentType
|
|
19
|
+
|
|
20
|
+
shell = AgentShell(agent_type=AgentType.CLAUDE_CODE)
|
|
21
|
+
|
|
22
|
+
response = await shell.execute(
|
|
23
|
+
cwd="/path/to/project",
|
|
24
|
+
prompt="Can you tell me about this project?",
|
|
25
|
+
allowed_tools=["Read", "Glob", "Grep"],
|
|
26
|
+
model="sonnet",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
print(response.response)
|
|
30
|
+
print(f"Cost: ${response.cost:.4f}")
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Stream
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from agent_shell.shell import AgentShell
|
|
37
|
+
from agent_shell.models.agent import AgentType
|
|
38
|
+
|
|
39
|
+
shell = AgentShell(agent_type=AgentType.CLAUDE_CODE)
|
|
40
|
+
|
|
41
|
+
async for event in shell.stream(
|
|
42
|
+
cwd="/path/to/project",
|
|
43
|
+
prompt="Refactor the auth module",
|
|
44
|
+
allowed_tools=["Read", "Edit", "Bash"],
|
|
45
|
+
model="sonnet",
|
|
46
|
+
effort="high",
|
|
47
|
+
include_thinking=True,
|
|
48
|
+
):
|
|
49
|
+
print(f"[{event.type}] {event.content}")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Supported CLI Agents:
|
|
53
|
+
|
|
54
|
+
- [x] Claude Code
|
|
55
|
+
- [ ] OpenCode
|
|
56
|
+
- [ ] Gemini CLI
|
|
57
|
+
- [ ] Copilot CLI
|
|
58
|
+
- [ ] Codex
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/agent_shell/__init__.py
|
|
4
|
+
src/agent_shell/shell.py
|
|
5
|
+
src/agent_shell/adapters/__init__.py
|
|
6
|
+
src/agent_shell/adapters/agent_adapter_protocol.py
|
|
7
|
+
src/agent_shell/adapters/claude_code_adapter.py
|
|
8
|
+
src/agent_shell/models/__init__.py
|
|
9
|
+
src/agent_shell/models/agent.py
|
|
10
|
+
src/agent_shell_py.egg-info/PKG-INFO
|
|
11
|
+
src/agent_shell_py.egg-info/SOURCES.txt
|
|
12
|
+
src/agent_shell_py.egg-info/dependency_links.txt
|
|
13
|
+
src/agent_shell_py.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
agent_shell
|