opalacoder 0.1.0__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.
opalacoder/vcs.py ADDED
@@ -0,0 +1,254 @@
1
+ """Version Control System (VCS) strategies for OpalaCoder."""
2
+
3
+ import os
4
+ import subprocess
5
+ from abc import ABC, abstractmethod
6
+ from typing import List, Callable
7
+ from agenticblocks.core.function_block import as_tool
8
+
9
+ from . import terminal as T
10
+ from .tools import AGENT_PROGRESS, _preview, get_project_path
11
+
12
+ # ─── Base Strategy ────────────────────────────────────────────────────────────
13
+
14
+ class VersionControlStrategy(ABC):
15
+ def __init__(self, project_path: str):
16
+ self.project_path = os.path.abspath(project_path)
17
+
18
+ @abstractmethod
19
+ def setup(self):
20
+ """Initialize VCS environment (e.g. create shadow git, gitignore)."""
21
+ pass
22
+
23
+ @abstractmethod
24
+ def pre_run(self, context_msg: str):
25
+ """Called before the agent starts executing its plan."""
26
+ pass
27
+
28
+ @abstractmethod
29
+ def post_run(self, success: bool, msg: str = ""):
30
+ """Called after the agent finishes its execution."""
31
+ pass
32
+
33
+ @abstractmethod
34
+ def get_tools(self) -> List[Callable]:
35
+ """Return a list of git tools available to the agent."""
36
+ pass
37
+
38
+ @abstractmethod
39
+ def manual_commit(self, message: str) -> tuple[bool, str]:
40
+ """Manually commit changes to the VCS."""
41
+ pass
42
+
43
+ @abstractmethod
44
+ def undo_last(self) -> tuple[bool, str]:
45
+ """Undo the last change in the VCS."""
46
+ pass
47
+
48
+
49
+ # ─── Shadow Git Helper ────────────────────────────────────────────────────────
50
+
51
+ def _run_shadow_git(command: str) -> subprocess.CompletedProcess:
52
+ """Run a Git command using the internal shadow git directory."""
53
+ project_path = get_project_path()
54
+ shadow_dir = os.path.join(project_path, ".opalacoder", ".git")
55
+
56
+ # Ensure git dir config is passed
57
+ full_cmd = f"git --git-dir={shadow_dir} --work-tree={project_path} {command}"
58
+
59
+ return subprocess.run(
60
+ full_cmd,
61
+ shell=True,
62
+ capture_output=True,
63
+ text=True,
64
+ cwd=project_path
65
+ )
66
+
67
+ def _init_shadow_git(project_path: str):
68
+ """Initialize the shadow git repository if it doesn't exist."""
69
+ shadow_base = os.path.join(project_path, ".opalacoder")
70
+ shadow_dir = os.path.join(shadow_base, ".git")
71
+ gitignore_path = os.path.join(shadow_base, ".gitignore")
72
+
73
+ os.makedirs(shadow_base, exist_ok=True)
74
+
75
+ if not os.path.exists(shadow_dir):
76
+ # Init repo
77
+ cmd = f"git --git-dir={shadow_dir} --work-tree={project_path} init"
78
+ subprocess.run(cmd, shell=True, capture_output=True, cwd=project_path)
79
+
80
+ # Configure excludes file
81
+ if not os.path.exists(gitignore_path):
82
+ with open(gitignore_path, "w", encoding="utf-8") as f:
83
+ f.write(".env\nnode_modules/\n__pycache__/\n.venv/\n")
84
+
85
+ cmd_exclude = f"git --git-dir={shadow_dir} --work-tree={project_path} config core.excludesFile {gitignore_path}"
86
+ subprocess.run(cmd_exclude, shell=True, capture_output=True, cwd=project_path)
87
+
88
+ # Initial commit
89
+ _run_shadow_git("add .")
90
+ _run_shadow_git("commit -m 'Initial checkpoint (Auto)'")
91
+
92
+ def _auto_checkpoint(message: str):
93
+ """Automatically create a checkpoint in the shadow git."""
94
+ _run_shadow_git("add .")
95
+ res = _run_shadow_git(f"commit -m '{message}'")
96
+ return res.returncode == 0
97
+
98
+
99
+ # ─── Agent Git Tools ──────────────────────────────────────────────────────────
100
+
101
+ @as_tool(name="git_status", description="Get the status of the internal version control. Shows modified/added files.")
102
+ def git_status() -> str:
103
+ AGENT_PROGRESS.update("git_status")
104
+ res = _run_shadow_git("status -s")
105
+ return res.stdout if res.stdout.strip() else "Working tree clean."
106
+
107
+ @as_tool(name="git_diff", description="Get the diff of the internal version control to see exact code changes.")
108
+ def git_diff() -> str:
109
+ AGENT_PROGRESS.update("git_diff")
110
+ res = _run_shadow_git("diff")
111
+ return res.stdout if res.stdout.strip() else "No changes."
112
+
113
+ @as_tool(name="git_commit", description="Commit all current changes to the internal version control. Use this to save milestones.")
114
+ def git_commit(message: str) -> str:
115
+ AGENT_PROGRESS.update("git_commit", _preview(message))
116
+ _run_shadow_git("add .")
117
+ res = _run_shadow_git(f'commit -m "{message}"')
118
+ if res.returncode == 0:
119
+ return f"Successfully committed: {message}"
120
+ return f"Failed to commit or nothing to commit. Stderr: {res.stderr}"
121
+
122
+
123
+ # ─── Concrete Strategies ──────────────────────────────────────────────────────
124
+
125
+ class AutoGitStrategy(VersionControlStrategy):
126
+ """Deterministic mode: Shadow Git initialized, pre/post checkpoints enforced, NO tools given to agent."""
127
+
128
+ def setup(self):
129
+ _init_shadow_git(self.project_path)
130
+ T.info("Modo Auto VCS: Repositório interno inicializado/verificado.")
131
+
132
+ def pre_run(self, context_msg: str):
133
+ _auto_checkpoint("Pre-run checkpoint: Before executing plan")
134
+
135
+ def post_run(self, success: bool, msg: str = ""):
136
+ status = "Success" if success else "Failed"
137
+ _auto_checkpoint(f"Post-run checkpoint: {status}. {msg}")
138
+
139
+ def get_tools(self) -> List[Callable]:
140
+ return [] # No tools for the agent in auto mode
141
+
142
+ def manual_commit(self, message: str) -> tuple[bool, str]:
143
+ _run_shadow_git("add .")
144
+ res = _run_shadow_git(f"commit -m '{message}'")
145
+ if res.returncode == 0:
146
+ return True, "Committed."
147
+ return False, res.stderr
148
+
149
+ def undo_last(self) -> tuple[bool, str]:
150
+ res = _run_shadow_git("rev-parse HEAD~1")
151
+ if res.returncode != 0:
152
+ return False, "Cannot undo. No previous checkpoints."
153
+ _run_shadow_git("reset --hard HEAD~1")
154
+ _run_shadow_git("clean -fd")
155
+ return True, "Last change undone."
156
+
157
+
158
+ class HybridGitStrategy(VersionControlStrategy):
159
+ """Hybrid mode: Shadow Git initialized, agent has tools, orchestrator ensures safety checkpoints."""
160
+
161
+ def setup(self):
162
+ _init_shadow_git(self.project_path)
163
+ T.info("Modo Hybrid VCS: Repositório interno pronto. Agente tem ferramentas de Git.")
164
+
165
+ def pre_run(self, context_msg: str):
166
+ _auto_checkpoint("Pre-run checkpoint: Before executing plan")
167
+
168
+ def post_run(self, success: bool, msg: str = ""):
169
+ status = "Success" if success else "Failed"
170
+ _auto_checkpoint(f"Post-run checkpoint: {status}. {msg}")
171
+
172
+ def get_tools(self) -> List[Callable]:
173
+ return [git_status, git_diff, git_commit]
174
+
175
+ def manual_commit(self, message: str) -> tuple[bool, str]:
176
+ _run_shadow_git("add .")
177
+ res = _run_shadow_git(f"commit -m '{message}'")
178
+ if res.returncode == 0:
179
+ return True, "Committed."
180
+ return False, res.stderr
181
+
182
+ def undo_last(self) -> tuple[bool, str]:
183
+ res = _run_shadow_git("rev-parse HEAD~1")
184
+ if res.returncode != 0:
185
+ return False, "Cannot undo. No previous checkpoints."
186
+ _run_shadow_git("reset --hard HEAD~1")
187
+ _run_shadow_git("clean -fd")
188
+ return True, "Last change undone."
189
+
190
+
191
+ class AgentDrivenGitStrategy(VersionControlStrategy):
192
+ """Agent-driven mode: Orchestrator does nothing automatically. Agent gets tools and decides when to use them."""
193
+
194
+ def setup(self):
195
+ _init_shadow_git(self.project_path)
196
+ T.info("Modo Agent-Driven VCS: Agente tem controle total sobre o repositório interno.")
197
+
198
+ def pre_run(self, context_msg: str):
199
+ pass
200
+
201
+ def post_run(self, success: bool, msg: str = ""):
202
+ pass
203
+
204
+ def get_tools(self) -> List[Callable]:
205
+ return [git_status, git_diff, git_commit]
206
+
207
+ def manual_commit(self, message: str) -> tuple[bool, str]:
208
+ _run_shadow_git("add .")
209
+ res = _run_shadow_git(f"commit -m '{message}'")
210
+ if res.returncode == 0:
211
+ return True, "Committed."
212
+ return False, res.stderr
213
+
214
+ def undo_last(self) -> tuple[bool, str]:
215
+ res = _run_shadow_git("rev-parse HEAD~1")
216
+ if res.returncode != 0:
217
+ return False, "Cannot undo. No previous checkpoints."
218
+ _run_shadow_git("reset --hard HEAD~1")
219
+ _run_shadow_git("clean -fd")
220
+ return True, "Last change undone."
221
+
222
+
223
+ class NoGitStrategy(VersionControlStrategy):
224
+ """Null strategy: No version control at all."""
225
+
226
+ def setup(self):
227
+ pass
228
+
229
+ def pre_run(self, context_msg: str):
230
+ pass
231
+
232
+ def post_run(self, success: bool, msg: str = ""):
233
+ pass
234
+
235
+ def get_tools(self) -> List[Callable]:
236
+ return []
237
+
238
+ def manual_commit(self, message: str) -> tuple[bool, str]:
239
+ return False, "VCS is disabled."
240
+
241
+ def undo_last(self) -> tuple[bool, str]:
242
+ return False, "VCS is disabled."
243
+
244
+
245
+ def get_vcs_strategy(strategy_name: str, project_path: str) -> VersionControlStrategy:
246
+ """Factory method to get the selected strategy."""
247
+ strategies = {
248
+ "auto": AutoGitStrategy,
249
+ "hybrid": HybridGitStrategy,
250
+ "agent_driven": AgentDrivenGitStrategy,
251
+ "none": NoGitStrategy
252
+ }
253
+ klass = strategies.get(strategy_name.lower(), HybridGitStrategy)
254
+ return klass(project_path)
@@ -0,0 +1,230 @@
1
+ Metadata-Version: 2.4
2
+ Name: opalacoder
3
+ Version: 0.1.0
4
+ Summary: Autonomous coding agent with interactive planning and modular execution
5
+ Project-URL: Homepage, https://github.com/gilzamir/OpalaCoder
6
+ Project-URL: Bug Tracker, https://github.com/gilzamir/OpalaCoder/issues
7
+ Author-email: Gil Zamir <gilzamir@example.com>
8
+ License: MIT
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: agenticblocks-io
14
+ Requires-Dist: instructor>=1.15.1
15
+ Requires-Dist: numpy>=1.21.0
16
+ Requires-Dist: python-dotenv>=1.0.0
17
+ Requires-Dist: pyyaml>=6.0
18
+ Requires-Dist: rich>=13.0.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ # OpalaCoder
22
+
23
+ **OpalaCoder** is an autonomous coding agent with interactive planning, modular execution, and persistent project memory. It is designed to work well with small and less autonomous models while maintaining the feel of a fully autonomous agent. It is built using the **AgenticBlocks.IO** framework.
24
+
25
+ ---
26
+
27
+ ## Features
28
+
29
+ ### Project-Centric Context Management
30
+ OpalaCoder centers around **projects** rather than transient chat sessions. Every interaction happens within a named project with a fixed filesystem path. This anchors the LLM context, loads only project-relevant skills, scopes all file and terminal operations, and persists history effectively for both small local models and large hosted APIs.
31
+
32
+ ### Advanced Semantic Router (Chain of Thought)
33
+ Uses an LLM for semantic routing with internal reasoning (Chain of Thought). It translates user demands to English internally, correctly deduces the intent, and seamlessly routes execution even for multilingual commands, injecting only the necessary context from specific **Skills** (`skills/*.md`).
34
+
35
+ ### Dynamic Model Selection (Double Inference Architecture)
36
+ OpalaCoder dynamically evaluates task complexity. For trivial tasks, it uses a fast default model (e.g., `ollama/mistral-nemo`). If the task requires architectural refactoring or advanced complex logic, it automatically falls back to a more powerful alternative model configured via API keys.
37
+
38
+ ### Interactive Planning
39
+ The agent receives a natural language demand, generates a high-level landscape plan, and enters a refinement loop with the user until the plan is approved. The approved plan is then automatically decomposed into executable sub-steps (subplans).
40
+
41
+ ### Execution with Retry & Strategy Routing
42
+ OpalaCoder delegates orchestration to specialized strategies. Small models use a deterministic execution pipeline (DAG), while capable models can use a fully autonomous agent loop. If a sub-step fails, the agent retries (up to a configurable limit) by injecting the previous error into the context for self-correction.
43
+
44
+ ### Execution Modes
45
+
46
+ | Mode | Behavior |
47
+ |--------|---------------|
48
+ | `plan` | Generates a plan and asks for user approval before executing (default) |
49
+ | `auto` | Executes everything without interruptions — ideal for automated pipelines |
50
+ | `edit` | Requests user confirmation only for sensitive operations (file creation/deletion, network calls, etc.) |
51
+
52
+ ### Persistent Projects and CLI Commands
53
+ Each execution belongs to a named project with continuous memory. During the chat with OpalaCoder, you can interact with the state manager using native commands:
54
+ - `/help` or `/h`: Shows available commands.
55
+ - `/clear`: Clears the memory and history of the current project.
56
+ - `/rename <new_name>`: Renames the active project.
57
+ - `/list`: Lists all projects saved in SQLite.
58
+ - `/load <name>` and `/delete <name>`: Loads or deletes old projects. (Deleting also asks to delete the project directory).
59
+ - `/skills`: Lists available skills and highlights the active ones for the project.
60
+ - `/addskill <name>` and `/rmskill <name>`: Adds or removes specific skills for the current project.
61
+ - `/undo`: Reverts the last change made by the agent via internal shadow VCS.
62
+ - `/commit <msg>`: Forces a commit to the local shadow git control.
63
+ - `/exit` or `/quit`: Exits the application.
64
+
65
+ ### Shadow Git (VCS)
66
+ Every project comes with an isolated "Shadow Git" (`.opalacoder/.git`) that automatically checkpoints the codebase before and after execution. This allows for safe iteration without muddying the user's main git repository, and enables the `/undo` command.
67
+
68
+ ### Elegant Terminal
69
+ Formatted output with [Rich](https://github.com/Texel-io/rich): banners, progress spinners, plan panels, per-subplan status tables, and highlighted error reports.
70
+
71
+ ### Modular Architecture
72
+ The code is divided into independent, easy-to-debug modules:
73
+
74
+ ```text
75
+ opalacoder/
76
+ ├── config.py Global settings (model, retries, mode, db, VCS)
77
+ ├── terminal.py Rich output (banners, spinners, panels, tables)
78
+ ├── project.py SQLite project management and state
79
+ ├── vcs.py Internal shadow Git strategies (auto, hybrid, agent-driven)
80
+ ├── agents.py LLM agent factories
81
+ ├── planner.py Pipeline: panorama → refinement → decomposition
82
+ ├── orchestrator.py Strategy-based execution (deterministic vs autonomous)
83
+ └── cli.py Argparse + project bootstrap + REPL
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Requirements
89
+
90
+ - Python 3.11+
91
+ - [agenticblocks](https://github.com/gilzamir/agenticblocks) installed in the virtual environment
92
+ - [Rich](https://github.com/Texel-io/rich): `pip install rich`
93
+ - An accessible LLM server (e.g., [Ollama](https://ollama.com) with `mistral-nemo`, or any model supported by [litellm](https://docs.litellm.ai))
94
+
95
+ ---
96
+
97
+ ## Installation
98
+
99
+ ```bash
100
+ # Clone the repository
101
+ git clone <repository-url>
102
+ cd OpalaCoder
103
+
104
+ # Create and activate the virtual environment
105
+ python -m venv .env
106
+ source .env/bin/activate # Linux/macOS
107
+ # .env\Scripts\activate # Windows
108
+
109
+ # Install the dependencies
110
+ pip install -r requirements.txt
111
+ ```
112
+
113
+ ### Environment Variables (Optional)
114
+
115
+ Create a `.env` file in the project root to override defaults:
116
+
117
+ ```env
118
+ # Default LLM model (any litellm supported string)
119
+ OPALA_MODEL=ollama/mistral-nemo
120
+ ```
121
+
122
+ ---
123
+
124
+ ## How to Run
125
+
126
+ ```bash
127
+ # Activate the virtual environment
128
+ source .env/bin/activate
129
+
130
+ # Default execution (plan mode)
131
+ python main.py
132
+
133
+ # Choose execution mode
134
+ python main.py --mode auto
135
+ python main.py --mode plan
136
+ python main.py --mode edit
137
+
138
+ # Use another model
139
+ python main.py --model ollama/llama3
140
+
141
+ # Custom database path
142
+ python main.py --db /path/to/projects.db
143
+
144
+ # Show version
145
+ python main.py --version
146
+
147
+ # Help
148
+ python main.py --help
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Project Flow
154
+
155
+ ```text
156
+ 1. Banner + Mode Selection
157
+
158
+ 2. Project Configuration
159
+ ├── New Project → Name, Path, Description -> LLM selects skills
160
+ └── Existing → Load context and skills
161
+
162
+ 3. User enters demand
163
+
164
+ 4. Agent generates landscape (high-level plan)
165
+
166
+ 5. Refinement loop (plan/edit modes)
167
+ ├── User approves → proceeds
168
+ └── User suggests changes → agent refines and loops back to step 5
169
+
170
+ 6. Decomposition into subplans (SP-1, SP-2, …)
171
+
172
+ 7. Sequential execution by dependency
173
+ └── For each subplan:
174
+ ├── Pre-run VCS checkpoint
175
+ ├── Executes generated code (AgenticBlocks WorkflowGraph)
176
+ ├── Success → Next subplan
177
+ └── Failure → Retry up to max_retries, then report error
178
+ ├── Post-run VCS checkpoint
179
+
180
+ 8. Aggregation: Final synthesized result of the operation
181
+
182
+ 9. Result displayed + project saved
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Advanced Configuration
188
+
189
+ ### Build & Test Commands
190
+ Run tests in the `tests` directory after implementing a new feature:
191
+
192
+ ```bash
193
+ python -m pytest
194
+ ```
195
+
196
+ ### Change the Default Model
197
+
198
+ Edit `opalacoder/config.py`:
199
+
200
+ ```python
201
+ DEFAULT_MODEL = "ollama/mistral-nemo" # change here
202
+ ```
203
+
204
+ Or use the environment variable `OPALA_MODEL`.
205
+
206
+ ### Sensitive Operations
207
+
208
+ In `opalacoder/config.py`:
209
+
210
+ ```python
211
+ SENSITIVE_OPS = {
212
+ "write_file", "delete_file", "run_shell",
213
+ "send_network_request", "create_user", "delete_user",
214
+ # add keywords here for operations that require confirmation in edit mode
215
+ }
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Security
221
+
222
+ - The `edit` mode requires explicit confirmation for operations affecting the filesystem, network, or user accounts.
223
+ - Generated code is executed locally. For greater isolation, the `CodePlanExecutorBlock` from the AgenticBlocks library supports execution in a Docker container — edit `make_executor_block` in `opalacoder/agents.py` changing `execution_mode="local"` to `execution_mode="docker"`.
224
+ - Never run the agent in `auto` mode with access to production systems without reviewing the generated subplans.
225
+
226
+ ---
227
+
228
+ ## License
229
+
230
+ MIT
@@ -0,0 +1,27 @@
1
+ opalacoder/__init__.py,sha256=8Bawhbei8CD1Y0IEU-BF73c6DZT-3Cxft5qNcaUPeog,106
2
+ opalacoder/agents.py,sha256=F45PyJ0utG3zDoVp_vbdB79gMZCm1aYCWuGQDA5oP8g,10651
3
+ opalacoder/api_keys.py,sha256=O8dXsKPLi-mUGNKkzX2ZuHmepi9Aj8TIROrkp3vN6e4,2526
4
+ opalacoder/cli.py,sha256=wDYeuIwdU9JxanCko9dfjxFJBmgIDBu6FiSXkZJcsjE,13957
5
+ opalacoder/cli_commands.py,sha256=ElOkySzZupYYk5M7VBaAuvhWQ-kSYcZvsbo1xAty0L4,11538
6
+ opalacoder/config.py,sha256=8m8MyB0B3mkYTgEfYuYW7DCmrvu-pl1iBzpJ3kHIxQA,7318
7
+ opalacoder/embeddings.py,sha256=6Z8reaUjFktkp6X80wH5k-vq3h2aBworC6dI-ocHieA,3031
8
+ opalacoder/i18n.py,sha256=pMMd25jDnGW8WW75fzPwJFoTq2Xj87vVEjXnmq7e1mg,15453
9
+ opalacoder/orchestrator.py,sha256=Vpb3AkB0ZmARSHx06RnIAWhhsdbGDmaRkf51CiPmePg,16325
10
+ opalacoder/planner.py,sha256=K8_bdxrMsL_TA-NHW51Dq0AlrspKOamp5SHUQwj2NNc,7839
11
+ opalacoder/project.py,sha256=TFw3cQsQbV7M9_YDy217k7bShnOTZov7yDSlAZ3UJyo,7912
12
+ opalacoder/session.py,sha256=Lxr4vB6-EMeMt_ygk_m_Oaz8AbQneXzdINf-X8nXOrM,215
13
+ opalacoder/skills.py,sha256=KjgpL8oYZKTM6RZCQWlicqzZ-NnzRPTYn8m2iSFTwHk,7740
14
+ opalacoder/structured.py,sha256=DjJvuFoVpc3PJARSwuCgT92E7z19zTsPK_XJ0fuE9Vo,4252
15
+ opalacoder/terminal.py,sha256=VQtBS1o_clS8nQ2rk372TLtTxUUIi-zjD0eNwwRNiCA,6846
16
+ opalacoder/tools.py,sha256=DPaIcUXDi0j83GVyD4niUE25cHgh9HvdcxxODLw6v6Y,13241
17
+ opalacoder/vcs.py,sha256=YhwYRYLqj1QfLJOagHGXRtcWc8RqbJQnhZMufgKH6Lg,9629
18
+ opalacoder/agents.yaml,sha256=8-Bz0FR5ZHtVsWvrA3rK4qbSMStdGxCnsCcwaBsQV44,2086
19
+ opalacoder/skills/generaldeveloper.md,sha256=nQovGfechp7VO2178F_mhFmB_HBBynjPiO3Tk1Ljhao,2895
20
+ opalacoder/skills/html_css_js.md,sha256=_0ofClJ7Wzah44BJKrsIXzHJ1Se56gt9Jt-fhv-oXqc,2703
21
+ opalacoder/skills/opalacoder.md,sha256=izpXE5RDkFYRwajVefoHM-tkPuk88l3_t-QBk_vsRhE,2257
22
+ opalacoder/skills/python_subprocess.md,sha256=g19gZZkczfDjwUD2LFZqQVQXZYKRkE5fVt0y7z5tZ_s,698
23
+ opalacoder/skills/react_vite.md,sha256=asVCnIBZ4HbIj2JLtKTAFaHSa2-44jvISXWjnl3u7WI,456
24
+ opalacoder-0.1.0.dist-info/METADATA,sha256=n-OAqeg1AvREC1_cAJrOk_VBdCrByo2g-zn9e2LDH9o,8660
25
+ opalacoder-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
26
+ opalacoder-0.1.0.dist-info/entry_points.txt,sha256=QRzRb2C0E2oetScVmSVhoXUJge_pN3kymfkKiWsxwYs,51
27
+ opalacoder-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ opalacoder = opalacoder.cli:main