pygent 0.1.12__py3-none-any.whl → 0.1.14__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.
pygent/__init__.py CHANGED
@@ -10,6 +10,7 @@ from .agent import Agent, run_interactive # noqa: E402,F401, must come after __
10
10
  from .models import Model, OpenAIModel # noqa: E402,F401
11
11
  from .errors import PygentError, APIError # noqa: E402,F401
12
12
  from .tools import register_tool, tool # noqa: E402,F401
13
+ from .task_manager import TaskManager # noqa: E402,F401
13
14
 
14
15
  __all__ = [
15
16
  "Agent",
@@ -20,4 +21,5 @@ __all__ = [
20
21
  "APIError",
21
22
  "register_tool",
22
23
  "tool",
24
+ "TaskManager",
23
25
  ]
pygent/agent.py CHANGED
@@ -20,10 +20,10 @@ DEFAULT_MODEL = os.getenv("PYGENT_MODEL", "gpt-4.1-mini")
20
20
  SYSTEM_MSG = (
21
21
  "You are Pygent, a sandboxed coding assistant.\n"
22
22
  "Respond with JSON when you need to use a tool."
23
- "If you need to stop, call the `stop` tool.\n"
23
+ "If you need to stop or finished you task, call the `stop` tool.\n"
24
24
  "You can use the following tools:\n"
25
25
  f"{json.dumps(tools.TOOL_SCHEMAS, indent=2)}\n"
26
- "You can also use the `continue` tool to continue the conversation.\n"
26
+ "You can also use the `continue` tool to request user input or continue the conversation.\n"
27
27
  )
28
28
 
29
29
  console = Console()
@@ -44,7 +44,10 @@ class Agent:
44
44
  self.history.append({"role": "system", "content": self.system_msg})
45
45
 
46
46
  def step(self, user_msg: str):
47
+ """Execute one round of interaction with the model."""
48
+
47
49
  self.history.append({"role": "user", "content": user_msg})
50
+
48
51
  assistant_msg = self.model.chat(
49
52
  self.history, self.model_name, tools.TOOL_SCHEMAS
50
53
  )
@@ -60,12 +63,36 @@ class Agent:
60
63
  console.print(Panel(markdown_response, title="Resposta do Agente", title_align="left", border_style="cyan"))
61
64
  return assistant_msg
62
65
 
63
- def run_until_stop(self, user_msg: str, max_steps: int = 10) -> None:
64
- """Run steps automatically until the model calls the ``stop`` tool or
65
- the step limit is reached."""
66
+ def run_until_stop(
67
+ self,
68
+ user_msg: str,
69
+ max_steps: int = 20,
70
+ step_timeout: float | None = None,
71
+ max_time: float | None = None,
72
+ ) -> None:
73
+ """Run steps until ``stop`` is called or limits are reached."""
74
+
75
+ if step_timeout is None:
76
+ env = os.getenv("PYGENT_STEP_TIMEOUT")
77
+ step_timeout = float(env) if env else None
78
+ if max_time is None:
79
+ env = os.getenv("PYGENT_TASK_TIMEOUT")
80
+ max_time = float(env) if env else None
81
+
66
82
  msg = user_msg
83
+ start = time.monotonic()
84
+ self._timed_out = False
67
85
  for _ in range(max_steps):
86
+ if max_time is not None and time.monotonic() - start > max_time:
87
+ self.history.append({"role": "system", "content": f"[timeout after {max_time}s]"})
88
+ self._timed_out = True
89
+ break
90
+ step_start = time.monotonic()
68
91
  assistant_msg = self.step(msg)
92
+ if step_timeout is not None and time.monotonic() - step_start > step_timeout:
93
+ self.history.append({"role": "system", "content": f"[timeout after {step_timeout}s]"})
94
+ self._timed_out = True
95
+ break
69
96
  calls = assistant_msg.tool_calls or []
70
97
  if any(c.function.name in ("stop", "continue") for c in calls):
71
98
  break
pygent/runtime.py CHANGED
@@ -92,6 +92,24 @@ class Runtime:
92
92
  p.write_text(content, encoding="utf-8")
93
93
  return f"Wrote {p.relative_to(self.base_dir)}"
94
94
 
95
+ def read_file(self, path: Union[str, Path], binary: bool = False) -> str:
96
+ """Return the contents of a file relative to the workspace."""
97
+
98
+ p = self.base_dir / path
99
+ if not p.exists():
100
+ return f"file {p.relative_to(self.base_dir)} not found"
101
+ data = p.read_bytes()
102
+ if binary:
103
+ import base64
104
+
105
+ return base64.b64encode(data).decode()
106
+ try:
107
+ return data.decode()
108
+ except UnicodeDecodeError:
109
+ import base64
110
+
111
+ return base64.b64encode(data).decode()
112
+
95
113
  def cleanup(self) -> None:
96
114
  if self._use_docker and self.container is not None:
97
115
  try:
pygent/task_manager.py ADDED
@@ -0,0 +1,125 @@
1
+ from __future__ import annotations
2
+
3
+ """Manage background tasks executed by sub-agents."""
4
+
5
+ import os
6
+ import shutil
7
+ import threading
8
+ import uuid
9
+ from dataclasses import dataclass, field
10
+ from typing import Callable, Dict, TYPE_CHECKING
11
+
12
+ from .runtime import Runtime
13
+
14
+ if TYPE_CHECKING: # pragma: no cover - for type hints only
15
+ from .agent import Agent
16
+
17
+
18
+ @dataclass
19
+ class Task:
20
+ """Represents a delegated task."""
21
+
22
+ id: str
23
+ agent: "Agent"
24
+ thread: threading.Thread
25
+ status: str = field(default="running")
26
+
27
+
28
+ class TaskManager:
29
+ """Launch agents asynchronously and track their progress."""
30
+
31
+ def __init__(
32
+ self,
33
+ agent_factory: Callable[[], "Agent"] | None = None,
34
+ max_tasks: int | None = None,
35
+ ) -> None:
36
+ from .agent import Agent # local import to avoid circular dependency
37
+
38
+ env_max = os.getenv("PYGENT_MAX_TASKS")
39
+ self.max_tasks = max_tasks if max_tasks is not None else int(env_max or "3")
40
+ self.agent_factory = agent_factory or Agent
41
+ self.tasks: Dict[str, Task] = {}
42
+ self._lock = threading.Lock()
43
+
44
+ def start_task(
45
+ self,
46
+ prompt: str,
47
+ parent_rt: Runtime,
48
+ files: list[str] | None = None,
49
+ parent_depth: int = 0,
50
+ step_timeout: float | None = None,
51
+ task_timeout: float | None = None,
52
+ ) -> str:
53
+ """Create a new agent and run ``prompt`` asynchronously."""
54
+
55
+ if parent_depth >= 1:
56
+ raise RuntimeError("nested delegation is not allowed")
57
+
58
+ with self._lock:
59
+ active = sum(t.status == "running" for t in self.tasks.values())
60
+ if active >= self.max_tasks:
61
+ raise RuntimeError(f"max {self.max_tasks} tasks reached")
62
+
63
+ if step_timeout is None:
64
+ env = os.getenv("PYGENT_STEP_TIMEOUT")
65
+ step_timeout = float(env) if env else 60*5 # default 5 minutes
66
+ if task_timeout is None:
67
+ env = os.getenv("PYGENT_TASK_TIMEOUT")
68
+ task_timeout = float(env) if env else 60*20 # default 20 minutes
69
+
70
+ agent = self.agent_factory()
71
+ setattr(agent.runtime, "task_depth", parent_depth + 1)
72
+ if files:
73
+ for fp in files:
74
+ src = parent_rt.base_dir / fp
75
+ dest = agent.runtime.base_dir / fp
76
+ if src.is_dir():
77
+ shutil.copytree(src, dest, dirs_exist_ok=True)
78
+ elif src.exists():
79
+ dest.parent.mkdir(parents=True, exist_ok=True)
80
+ shutil.copy(src, dest)
81
+ task_id = uuid.uuid4().hex[:8]
82
+ task = Task(id=task_id, agent=agent, thread=None) # type: ignore[arg-type]
83
+
84
+ def run() -> None:
85
+ try:
86
+ agent.run_until_stop(
87
+ prompt,
88
+ step_timeout=step_timeout,
89
+ max_time=task_timeout,
90
+ )
91
+ if getattr(agent, "_timed_out", False):
92
+ task.status = f"timeout after {task_timeout}s"
93
+ else:
94
+ task.status = "finished"
95
+ except Exception as exc: # pragma: no cover - error propagation
96
+ task.status = f"error: {exc}"
97
+
98
+ t = threading.Thread(target=run, daemon=True)
99
+ task.thread = t
100
+ with self._lock:
101
+ self.tasks[task_id] = task
102
+ t.start()
103
+ return task_id
104
+
105
+ def status(self, task_id: str) -> str:
106
+ with self._lock:
107
+ task = self.tasks.get(task_id)
108
+ if not task:
109
+ return f"Task {task_id} not found"
110
+ return task.status
111
+
112
+ def collect_file(self, rt: Runtime, task_id: str, path: str) -> str:
113
+ """Copy a file from a task workspace into ``rt``."""
114
+
115
+ with self._lock:
116
+ task = self.tasks.get(task_id)
117
+ if not task:
118
+ return f"Task {task_id} not found"
119
+ src = task.agent.runtime.base_dir / path
120
+ if not src.exists():
121
+ return f"file {path} not found"
122
+ dest = rt.base_dir / path
123
+ dest.parent.mkdir(parents=True, exist_ok=True)
124
+ shutil.copy(src, dest)
125
+ return f"Retrieved {dest.relative_to(rt.base_dir)}"
pygent/tools.py CHANGED
@@ -5,6 +5,16 @@ import json
5
5
  from typing import Any, Callable, Dict, List
6
6
 
7
7
  from .runtime import Runtime
8
+ from .task_manager import TaskManager
9
+
10
+ _task_manager: TaskManager | None = None
11
+
12
+
13
+ def _get_manager() -> TaskManager:
14
+ global _task_manager
15
+ if _task_manager is None:
16
+ _task_manager = TaskManager()
17
+ return _task_manager
8
18
 
9
19
 
10
20
  # ---- registry ----
@@ -82,7 +92,7 @@ def _write_file(rt: Runtime, path: str, content: str) -> str:
82
92
 
83
93
  @tool(
84
94
  name="stop",
85
- description="Stop the autonomous loop.",
95
+ description="Stop the autonomous loop. This is a side-effect free tool that does not return any output. Use when finished some task or when you want to stop the agent.",
86
96
  parameters={"type": "object", "properties": {}},
87
97
  )
88
98
  def _stop(rt: Runtime) -> str: # pragma: no cover - side-effect free
@@ -91,9 +101,96 @@ def _stop(rt: Runtime) -> str: # pragma: no cover - side-effect free
91
101
 
92
102
  @tool(
93
103
  name="continue",
94
- description="Continue the conversation.",
104
+ description="Request user answer or input. If in your previous message you asked for user input, you can use this tool to continue the conversation.",
95
105
  parameters={"type": "object", "properties": {}},
96
106
  )
97
107
  def _continue(rt: Runtime) -> str: # pragma: no cover - side-effect free
98
108
  return "Continuing the conversation."
99
109
 
110
+
111
+
112
+
113
+ @tool(
114
+ name="delegate_task",
115
+ description="Create a background task using a new agent and return its ID.",
116
+ parameters={
117
+ "type": "object",
118
+ "properties": {
119
+ "prompt": {"type": "string", "description": "Instruction for the sub-agent"},
120
+ "files": {
121
+ "type": "array",
122
+ "items": {"type": "string"},
123
+ "description": "Files to copy to the sub-agent before starting",
124
+ },
125
+ "timeout": {"type": "number", "description": "Max seconds for the task"},
126
+ "step_timeout": {"type": "number", "description": "Time limit per step"},
127
+ },
128
+ "required": ["prompt"],
129
+ },
130
+ )
131
+ def _delegate_task(
132
+ rt: Runtime,
133
+ prompt: str,
134
+ files: list[str] | None = None,
135
+ timeout: float | None = None,
136
+ step_timeout: float | None = None,
137
+ ) -> str:
138
+ if getattr(rt, "task_depth", 0) >= 1:
139
+ return "error: delegation not allowed in sub-tasks"
140
+ try:
141
+ tid = _get_manager().start_task(
142
+ prompt,
143
+ parent_rt=rt,
144
+ files=files,
145
+ parent_depth=getattr(rt, "task_depth", 0),
146
+ step_timeout=step_timeout,
147
+ task_timeout=timeout,
148
+ )
149
+ except RuntimeError as exc:
150
+ return str(exc)
151
+ return f"started {tid}"
152
+
153
+
154
+ @tool(
155
+ name="task_status",
156
+ description="Check the status of a delegated task.",
157
+ parameters={
158
+ "type": "object",
159
+ "properties": {"task_id": {"type": "string"}},
160
+ "required": ["task_id"],
161
+ },
162
+ )
163
+ def _task_status(rt: Runtime, task_id: str) -> str:
164
+ return _get_manager().status(task_id)
165
+
166
+
167
+ @tool(
168
+ name="collect_file",
169
+ description="Retrieve a file from a delegated task into the main workspace.",
170
+ parameters={
171
+ "type": "object",
172
+ "properties": {
173
+ "task_id": {"type": "string"},
174
+ "path": {"type": "string"},
175
+ },
176
+ "required": ["task_id", "path"],
177
+ },
178
+ )
179
+ def _collect_file(rt: Runtime, task_id: str, path: str) -> str:
180
+ return _get_manager().collect_file(rt, task_id, path)
181
+
182
+
183
+ @tool(
184
+ name="download_file",
185
+ description="Return the contents of a file from the workspace (base64 if binary)",
186
+ parameters={
187
+ "type": "object",
188
+ "properties": {
189
+ "path": {"type": "string"},
190
+ "binary": {"type": "boolean", "default": False},
191
+ },
192
+ "required": ["path"],
193
+ },
194
+ )
195
+ def _download_file(rt: Runtime, path: str, binary: bool = False) -> str:
196
+ return rt.read_file(path, binary=binary)
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: pygent
3
+ Version: 0.1.14
4
+ Summary: Pygent is a minimalist coding assistant that runs commands in a Docker container when available and falls back to local execution. See https://marianochaves.github.io/pygent for documentation and https://github.com/marianochaves/pygent for the source code.
5
+ Author-email: Mariano Chaves <mchaves.software@gmail.com>
6
+ Project-URL: Documentation, https://marianochaves.github.io/pygent
7
+ Project-URL: Repository, https://github.com/marianochaves/pygent
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: rich>=13.7.0
12
+ Requires-Dist: openai>=1.0.0
13
+ Provides-Extra: test
14
+ Requires-Dist: pytest; extra == "test"
15
+ Provides-Extra: docs
16
+ Requires-Dist: mkdocs; extra == "docs"
17
+ Provides-Extra: docker
18
+ Requires-Dist: docker>=7.0.0; extra == "docker"
19
+ Provides-Extra: ui
20
+ Requires-Dist: gradio; extra == "ui"
21
+ Dynamic: license-file
22
+
23
+ # Pygent
24
+
25
+ Pygent is a coding assistant that executes each request inside an isolated Docker container whenever possible. If Docker is unavailable (for instance on some Windows setups) the commands are executed locally instead. Full documentation is available in the `docs/` directory and at [marianochaves.github.io/pygent](https://marianochaves.github.io/pygent/).
26
+
27
+ ## Features
28
+
29
+ * Runs commands in ephemeral containers (default image `python:3.12-slim`).
30
+ * Integrates with OpenAI-compatible models to orchestrate each step.
31
+ * Persists the conversation history during the session.
32
+ * Provides a small Python API for use in other projects.
33
+ * Optional web interface via `pygent-ui`.
34
+ * Register your own tools and customise the system prompt.
35
+
36
+ ## Installation
37
+
38
+ Installing from source is recommended:
39
+
40
+ ```bash
41
+ pip install -e .
42
+ ```
43
+
44
+ Python ≥ 3.9 is required. The package now bundles the `openai` client for model access.
45
+ To run commands in Docker containers also install `pygent[docker]`.
46
+
47
+ ## Configuration
48
+
49
+ Behaviour can be adjusted via environment variables (see `docs/configuration.md` for a complete list):
50
+
51
+ * `OPENAI_API_KEY` &ndash; key used to access the OpenAI API.
52
+ Set this to your API key or a key from any compatible provider.
53
+ * `OPENAI_BASE_URL` &ndash; base URL for OpenAI-compatible APIs
54
+ (defaults to ``https://api.openai.com/v1``).
55
+ * `PYGENT_MODEL` &ndash; model name used for requests (default `gpt-4.1-mini`).
56
+ * `PYGENT_IMAGE` &ndash; Docker image to create the container (default `python:3.12-slim`).
57
+ * `PYGENT_USE_DOCKER` &ndash; set to `0` to disable Docker and run locally.
58
+ * `PYGENT_MAX_TASKS` &ndash; maximum number of concurrent delegated tasks (default `3`).
59
+
60
+ ## CLI usage
61
+
62
+ After installing run:
63
+
64
+ ```bash
65
+ pygent
66
+ ```
67
+
68
+ Use `--docker` to run commands inside a container (requires
69
+ `pygent[docker]`). Use `--no-docker` or set `PYGENT_USE_DOCKER=0`
70
+ to force local execution.
71
+
72
+ Type messages normally; use `/exit` to end the session. Each command is executed
73
+ in the container and the result shown in the terminal.
74
+ Interactive programs that expect input (e.g. running `python` without a script)
75
+ are not supported and will exit immediately.
76
+ For a minimal web interface run `pygent-ui` instead (requires `pygent[ui]`).
77
+
78
+
79
+ ## API usage
80
+
81
+ You can also interact directly with the Python code:
82
+
83
+ ```python
84
+ from pygent import Agent
85
+
86
+ ag = Agent()
87
+ ag.step("echo 'Hello World'")
88
+ # ... more steps
89
+ ag.runtime.cleanup()
90
+ ```
91
+
92
+ See the [examples](https://github.com/marianochaves/pygent/tree/main/examples) folder for more complete scripts. Models can be swapped by
93
+ passing an object implementing the ``Model`` interface when creating the
94
+ ``Agent``. The default uses an OpenAI-compatible API, but custom models are
95
+ easy to plug in.
96
+
97
+ ### Using OpenAI and other providers
98
+
99
+ Set your OpenAI key:
100
+
101
+ ```bash
102
+ export OPENAI_API_KEY="sk-..."
103
+ ```
104
+
105
+ To use a different provider, set `OPENAI_BASE_URL` to the provider
106
+ endpoint and keep `OPENAI_API_KEY` pointing to the correct key:
107
+
108
+ ```bash
109
+ export OPENAI_BASE_URL="https://openrouter.ai/api/v1"
110
+ export OPENAI_API_KEY="your-provider-key"
111
+ ```
112
+
113
+ ## Development
114
+
115
+ 1. Install the test dependencies:
116
+
117
+ ```bash
118
+ pip install -e .[test]
119
+ ```
120
+
121
+ 2. Run the test suite:
122
+
123
+ ```bash
124
+ pytest
125
+ ```
126
+
127
+ Use `mkdocs serve` to build the documentation locally.
128
+
129
+ ## License
130
+
131
+ This project is released under the MIT license. See the `LICENSE` file for details.
132
+
@@ -0,0 +1,18 @@
1
+ pygent/__init__.py,sha256=8TB-wzDeQV0YANON-q7sUkzlIyAmTN6gyz4R-axmoYU,724
2
+ pygent/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
3
+ pygent/agent.py,sha256=5aY5HRlnLNwZzU_JD4bdEd9P9JLK3mtV4NJl7go9igY,4077
4
+ pygent/cli.py,sha256=Hz2FZeNMVhxoT5DjCqphXla3TisGJtPEz921LEcpxrA,527
5
+ pygent/errors.py,sha256=s5FBg_v94coSgMh7cfkP4hVXafViGYgCY8QiT698-c4,155
6
+ pygent/models.py,sha256=j3670gjUtvQRGZ5wqGDcQ7ZJVTdT5WiwL7nWTokeYzg,1141
7
+ pygent/openai_compat.py,sha256=cyWFtXt6sDfOlsZd3FuRxbcZMm3WU-DLPBQpbmcuiW8,2617
8
+ pygent/py.typed,sha256=0Wh72UpGSn4lSGW-u3xMV9kxcBHMdwE15IGUqiJTwqo,52
9
+ pygent/runtime.py,sha256=KckSdTSBIooz181ef8krXbSgqTxIaYDMdCy0ryOi7KQ,4386
10
+ pygent/task_manager.py,sha256=kQ2wwBkhPtpluPzKe9T9Hiv1NoxUA3-sEvuIiPqxq90,4167
11
+ pygent/tools.py,sha256=xHpUpG2QcBEvwTilpw8BDlYWY3KG9mItSt0FoKNkohA,5585
12
+ pygent/ui.py,sha256=xqPAvweghPOBBvoD72HzhN6zlXew_3inb8AN7Ck2zpQ,1328
13
+ pygent-0.1.14.dist-info/licenses/LICENSE,sha256=rIktBU2VR4kHzsWul64cbom2zHIgGqYmABoZwSur6T8,1071
14
+ pygent-0.1.14.dist-info/METADATA,sha256=Et3MB9nI0I9Wb9eWEAmKUrd52rS6H9TluAarCzQsiOk,4278
15
+ pygent-0.1.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ pygent-0.1.14.dist-info/entry_points.txt,sha256=b9j216E5UpuMrQWRZrwyEmacNEAYvw1tCKkZqdIVIOc,70
17
+ pygent-0.1.14.dist-info/top_level.txt,sha256=P26IYsb-ThK5IkGP_bRuGJQ0Q_Y8JCcbYqVpvULdxDw,7
18
+ pygent-0.1.14.dist-info/RECORD,,
@@ -1,20 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: pygent
3
- Version: 0.1.12
4
- Summary: Pygent is a minimalist coding assistant that runs commands in a Docker container when available and falls back to local execution. See https://marianochaves.github.io/pygent for documentation and https://github.com/marianochaves/pygent for the source code.
5
- Author-email: Mariano Chaves <mchaves.software@gmail.com>
6
- Project-URL: Documentation, https://marianochaves.github.io/pygent
7
- Project-URL: Repository, https://github.com/marianochaves/pygent
8
- Requires-Python: >=3.9
9
- License-File: LICENSE
10
- Requires-Dist: rich>=13.7.0
11
- Requires-Dist: openai>=1.0.0
12
- Provides-Extra: test
13
- Requires-Dist: pytest; extra == "test"
14
- Provides-Extra: docs
15
- Requires-Dist: mkdocs; extra == "docs"
16
- Provides-Extra: docker
17
- Requires-Dist: docker>=7.0.0; extra == "docker"
18
- Provides-Extra: ui
19
- Requires-Dist: gradio; extra == "ui"
20
- Dynamic: license-file
@@ -1,17 +0,0 @@
1
- pygent/__init__.py,sha256=L4IRxHYfzucHUjSMvYHG-1uoTe6XaYJkaHLXuGs4CI8,648
2
- pygent/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
3
- pygent/agent.py,sha256=ZyY9dEvB6bnM1ZFLh4XKQJ68v8xpRlP3HM2RRm6Y6Q0,3030
4
- pygent/cli.py,sha256=Hz2FZeNMVhxoT5DjCqphXla3TisGJtPEz921LEcpxrA,527
5
- pygent/errors.py,sha256=s5FBg_v94coSgMh7cfkP4hVXafViGYgCY8QiT698-c4,155
6
- pygent/models.py,sha256=j3670gjUtvQRGZ5wqGDcQ7ZJVTdT5WiwL7nWTokeYzg,1141
7
- pygent/openai_compat.py,sha256=cyWFtXt6sDfOlsZd3FuRxbcZMm3WU-DLPBQpbmcuiW8,2617
8
- pygent/py.typed,sha256=0Wh72UpGSn4lSGW-u3xMV9kxcBHMdwE15IGUqiJTwqo,52
9
- pygent/runtime.py,sha256=xD77DPFUfmbAwsIA5SjlEmj_TimFtN3j-6h53aUVdF0,3821
10
- pygent/tools.py,sha256=H2oice1Dyl-rw6Rq9hgWxpGSlxDD3ylCpxha8-4dkZQ,2568
11
- pygent/ui.py,sha256=xqPAvweghPOBBvoD72HzhN6zlXew_3inb8AN7Ck2zpQ,1328
12
- pygent-0.1.12.dist-info/licenses/LICENSE,sha256=rIktBU2VR4kHzsWul64cbom2zHIgGqYmABoZwSur6T8,1071
13
- pygent-0.1.12.dist-info/METADATA,sha256=896zkz6xObtzwzVoM77ts8BYMgx-lNQzuUCe8YkBv74,878
14
- pygent-0.1.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- pygent-0.1.12.dist-info/entry_points.txt,sha256=b9j216E5UpuMrQWRZrwyEmacNEAYvw1tCKkZqdIVIOc,70
16
- pygent-0.1.12.dist-info/top_level.txt,sha256=P26IYsb-ThK5IkGP_bRuGJQ0Q_Y8JCcbYqVpvULdxDw,7
17
- pygent-0.1.12.dist-info/RECORD,,