hexicodes 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.
- hexicodes-0.1.0/LICENSE +21 -0
- hexicodes-0.1.0/PKG-INFO +232 -0
- hexicodes-0.1.0/README.md +184 -0
- hexicodes-0.1.0/pyproject.toml +81 -0
- hexicodes-0.1.0/setup.cfg +4 -0
- hexicodes-0.1.0/src/hexi/__init__.py +2 -0
- hexicodes-0.1.0/src/hexi/adapters/__init__.py +21 -0
- hexicodes-0.1.0/src/hexi/adapters/event_log_jsonl.py +17 -0
- hexicodes-0.1.0/src/hexi/adapters/events_console.py +37 -0
- hexicodes-0.1.0/src/hexi/adapters/exec_local.py +16 -0
- hexicodes-0.1.0/src/hexi/adapters/memory_file.py +224 -0
- hexicodes-0.1.0/src/hexi/adapters/model_anthropic_compat.py +35 -0
- hexicodes-0.1.0/src/hexi/adapters/model_http_common.py +19 -0
- hexicodes-0.1.0/src/hexi/adapters/model_openai_compat.py +23 -0
- hexicodes-0.1.0/src/hexi/adapters/model_openrouter_http.py +135 -0
- hexicodes-0.1.0/src/hexi/adapters/model_openrouter_sdk.py +36 -0
- hexicodes-0.1.0/src/hexi/adapters/workspace_local_git.py +72 -0
- hexicodes-0.1.0/src/hexi/cli.py +739 -0
- hexicodes-0.1.0/src/hexi/core/__init__.py +11 -0
- hexicodes-0.1.0/src/hexi/core/domain.py +41 -0
- hexicodes-0.1.0/src/hexi/core/policy.py +30 -0
- hexicodes-0.1.0/src/hexi/core/ports.py +52 -0
- hexicodes-0.1.0/src/hexi/core/schemas.py +122 -0
- hexicodes-0.1.0/src/hexi/core/service.py +181 -0
- hexicodes-0.1.0/src/hexicodes.egg-info/PKG-INFO +232 -0
- hexicodes-0.1.0/src/hexicodes.egg-info/SOURCES.txt +39 -0
- hexicodes-0.1.0/src/hexicodes.egg-info/dependency_links.txt +1 -0
- hexicodes-0.1.0/src/hexicodes.egg-info/entry_points.txt +2 -0
- hexicodes-0.1.0/src/hexicodes.egg-info/requires.txt +27 -0
- hexicodes-0.1.0/src/hexicodes.egg-info/top_level.txt +1 -0
- hexicodes-0.1.0/tests/test_actionplan_parser.py +22 -0
- hexicodes-0.1.0/tests/test_cli.py +136 -0
- hexicodes-0.1.0/tests/test_event_log_jsonl.py +20 -0
- hexicodes-0.1.0/tests/test_exec_local.py +43 -0
- hexicodes-0.1.0/tests/test_memory_file.py +94 -0
- hexicodes-0.1.0/tests/test_models.py +64 -0
- hexicodes-0.1.0/tests/test_openrouter_models.py +157 -0
- hexicodes-0.1.0/tests/test_policy.py +17 -0
- hexicodes-0.1.0/tests/test_service.py +156 -0
- hexicodes-0.1.0/tests/test_workspace.py +15 -0
- hexicodes-0.1.0/tests/test_workspace_local_git.py +42 -0
hexicodes-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Antonio Ognio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
hexicodes-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hexicodes
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Minimal contract-driven coding-agent runtime + CLI
|
|
5
|
+
Author: Antonio Ognio
|
|
6
|
+
Maintainer: Antonio Ognio
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://hexi.readthedocs.io
|
|
9
|
+
Project-URL: Documentation, https://hexi.readthedocs.io
|
|
10
|
+
Project-URL: Source, https://github.com/antonioognio/hexi
|
|
11
|
+
Project-URL: Issues, https://github.com/antonioognio/hexi/issues
|
|
12
|
+
Keywords: ai,agent,coding-agent,cli,developer-tools,hexagonal-architecture,openrouter,typer
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: typer<1,>=0.12
|
|
28
|
+
Requires-Dist: httpx<1,>=0.27
|
|
29
|
+
Requires-Dist: rich<15,>=13.7
|
|
30
|
+
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
|
|
31
|
+
Provides-Extra: openrouter-http
|
|
32
|
+
Requires-Dist: requests<3,>=2.31; extra == "openrouter-http"
|
|
33
|
+
Provides-Extra: openrouter-sdk
|
|
34
|
+
Requires-Dist: openrouter<1,>=0.6.0; extra == "openrouter-sdk"
|
|
35
|
+
Provides-Extra: openrouter
|
|
36
|
+
Requires-Dist: requests<3,>=2.31; extra == "openrouter"
|
|
37
|
+
Requires-Dist: openrouter<1,>=0.6.0; extra == "openrouter"
|
|
38
|
+
Provides-Extra: docs
|
|
39
|
+
Requires-Dist: mkdocs<2,>=1.6; extra == "docs"
|
|
40
|
+
Requires-Dist: mkdocs-material<10,>=9.5; extra == "docs"
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: pytest<9,>=8.2; extra == "dev"
|
|
43
|
+
Requires-Dist: pytest-asyncio<1,>=0.23; extra == "dev"
|
|
44
|
+
Requires-Dist: requests<3,>=2.31; extra == "dev"
|
|
45
|
+
Requires-Dist: responses<1,>=0.25; extra == "dev"
|
|
46
|
+
Requires-Dist: respx<1,>=0.21; extra == "dev"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
# Hexi v0.1.0
|
|
50
|
+
|
|
51
|
+
Hexi is a minimal, contract-driven (hexagonal) coding-agent runtime and CLI.
|
|
52
|
+
It runs exactly one agent step per invocation against a local git repository.
|
|
53
|
+
|
|
54
|
+
## Test Drive (5 Minutes)
|
|
55
|
+
Run this in any local git repo you can safely modify.
|
|
56
|
+
|
|
57
|
+
1. Install Hexi:
|
|
58
|
+
```bash
|
|
59
|
+
pip install -e .
|
|
60
|
+
```
|
|
61
|
+
Optional OpenRouter support:
|
|
62
|
+
```bash
|
|
63
|
+
pip install -e ".[openrouter]"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
2. Initialize Hexi files:
|
|
67
|
+
```bash
|
|
68
|
+
hexi init
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
3. Onboard provider/model and key:
|
|
72
|
+
```bash
|
|
73
|
+
hexi onboard
|
|
74
|
+
```
|
|
75
|
+
When prompted, pick any provider. For OpenRouter providers, install the optional extra and provide `OPENROUTER_API_KEY`.
|
|
76
|
+
|
|
77
|
+
4. Verify setup:
|
|
78
|
+
```bash
|
|
79
|
+
hexi doctor
|
|
80
|
+
```
|
|
81
|
+
Expected: provider/model printed and `Doctor check passed`.
|
|
82
|
+
|
|
83
|
+
5. Run one agent step:
|
|
84
|
+
```bash
|
|
85
|
+
hexi run "Add one tiny test for an existing function and run pytest"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
6. Inspect what changed:
|
|
89
|
+
```bash
|
|
90
|
+
hexi diff
|
|
91
|
+
tail -n 20 .hexi/runlog.jsonl
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
7. If you want to switch providers later:
|
|
95
|
+
```bash
|
|
96
|
+
hexi onboard
|
|
97
|
+
```
|
|
98
|
+
Re-run onboarding to update `.hexi/local.toml`.
|
|
99
|
+
|
|
100
|
+
## What it is
|
|
101
|
+
- Python package (PyPI distribution): `hexicodes`
|
|
102
|
+
- Core contracts in `hexi.core`
|
|
103
|
+
- Side-effect adapters in `hexi.adapters`
|
|
104
|
+
- One-step execution with structured event logging to `.hexi/runlog.jsonl`
|
|
105
|
+
|
|
106
|
+
## What it is not
|
|
107
|
+
- No daemon, no background workers, no web UI
|
|
108
|
+
- No MCP server and no SQLite in v0.1.0
|
|
109
|
+
- No multi-agent orchestration
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## Install
|
|
113
|
+
```bash
|
|
114
|
+
pip install -e .
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### OpenRouter support (optional)
|
|
118
|
+
- HTTP adapter only (`openrouter_http` provider):
|
|
119
|
+
```bash
|
|
120
|
+
pip install -e ".[openrouter-http]"
|
|
121
|
+
```
|
|
122
|
+
- SDK adapter only (`openrouter_sdk` provider):
|
|
123
|
+
```bash
|
|
124
|
+
pip install -e ".[openrouter-sdk]"
|
|
125
|
+
```
|
|
126
|
+
- Both OpenRouter adapters:
|
|
127
|
+
```bash
|
|
128
|
+
pip install -e ".[openrouter]"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Dev/test dependencies:
|
|
132
|
+
```bash
|
|
133
|
+
pip install -e ".[dev]"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## CLI
|
|
137
|
+
- `hexi --help` or `hexi help` : show command help
|
|
138
|
+
- `hexi --version` or `hexi version` : print installed version
|
|
139
|
+
- `hexi init` : create `.hexi/config.toml`, `.hexi/local.toml`, `.hexi/runlog.jsonl`
|
|
140
|
+
- `hexi onboard` : interactive setup for provider/model and optional local key paste
|
|
141
|
+
- `hexi new` : scaffold a project from built-in Hexi templates (non-interactive by default)
|
|
142
|
+
- `hexi demo` : fancy interactive flow with random/model-generated ideas and template scaffolding
|
|
143
|
+
- `hexi run "<task>"` : execute one agent step and emit structured events
|
|
144
|
+
- `hexi diff` : show current git diff
|
|
145
|
+
- `hexi doctor` : verbose diagnostics; use `--probe-model` for live “What model are you?” check
|
|
146
|
+
- `hexi plan-check --file plan.json` : validate/troubleshoot ActionPlan JSON directly
|
|
147
|
+
|
|
148
|
+
## Documentation (MkDocs + Read the Docs)
|
|
149
|
+
Build docs locally:
|
|
150
|
+
```bash
|
|
151
|
+
pip install -e ".[docs]"
|
|
152
|
+
mkdocs serve
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Read the Docs config is in `.readthedocs.yml`.
|
|
156
|
+
|
|
157
|
+
## Configuration design choices
|
|
158
|
+
Hexi uses layered TOML configuration:
|
|
159
|
+
1. `.hexi/config.toml` (repo defaults)
|
|
160
|
+
2. `.hexi/local.toml` (local machine overrides)
|
|
161
|
+
3. Environment variables (recommended for secrets)
|
|
162
|
+
|
|
163
|
+
For secrets, env vars are preferred. `hexi onboard` can write keys to `.hexi/local.toml` for local/testing convenience.
|
|
164
|
+
|
|
165
|
+
## Config shape (`.hexi/config.toml`)
|
|
166
|
+
```toml
|
|
167
|
+
[model]
|
|
168
|
+
provider = "openai_compat" # openrouter_http | openrouter_sdk | openai_compat | anthropic_compat
|
|
169
|
+
model = "gpt-4o-mini"
|
|
170
|
+
|
|
171
|
+
[providers.openrouter_http]
|
|
172
|
+
base_url = "https://openrouter.ai/api/v1"
|
|
173
|
+
api_style = "openai" # openai | anthropic
|
|
174
|
+
|
|
175
|
+
[providers.openrouter_sdk]
|
|
176
|
+
base_url = "https://openrouter.ai/api/v1"
|
|
177
|
+
|
|
178
|
+
[providers.openai_compat]
|
|
179
|
+
base_url = "https://api.openai.com/v1"
|
|
180
|
+
|
|
181
|
+
[providers.anthropic_compat]
|
|
182
|
+
base_url = "https://api.anthropic.com"
|
|
183
|
+
|
|
184
|
+
[policy]
|
|
185
|
+
allow_commands = ["git status", "git diff", "pytest", "python -m pytest"]
|
|
186
|
+
max_diff_chars = 4000
|
|
187
|
+
max_file_read_chars = 4000
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Local override example (`.hexi/local.toml`)
|
|
191
|
+
```toml
|
|
192
|
+
[model]
|
|
193
|
+
provider = "openrouter_http"
|
|
194
|
+
model = "anthropic/claude-sonnet-4-6"
|
|
195
|
+
|
|
196
|
+
[providers.openrouter_http]
|
|
197
|
+
api_style = "anthropic"
|
|
198
|
+
|
|
199
|
+
[secrets]
|
|
200
|
+
openrouter_api_key = "..."
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Env vars
|
|
204
|
+
- `OPENROUTER_API_KEY` for `openrouter_http` and `openrouter_sdk`
|
|
205
|
+
- `OPENAI_API_KEY` for `openai_compat`
|
|
206
|
+
- `ANTHROPIC_API_KEY` for `anthropic_compat`
|
|
207
|
+
|
|
208
|
+
## Packaging
|
|
209
|
+
- Distribution name: `hexicodes`
|
|
210
|
+
- Console script: `hexi`
|
|
211
|
+
- Optional extras:
|
|
212
|
+
- `openrouter-http`
|
|
213
|
+
- `openrouter-sdk`
|
|
214
|
+
- `openrouter`
|
|
215
|
+
- `docs`
|
|
216
|
+
- `dev`
|
|
217
|
+
|
|
218
|
+
## Included example projects
|
|
219
|
+
- `examples/todo_refiner` : minimal CLI-wrapper agent integration
|
|
220
|
+
- `examples/embedded_step` : direct embedded `RunStepService` usage
|
|
221
|
+
- `examples/policy_loop` : multi-step user-gated loop using repeated `hexi run`
|
|
222
|
+
|
|
223
|
+
## Included Hexi-native templates
|
|
224
|
+
- `templates/hexi-python-lib` : tested library starter with Hexi wiring
|
|
225
|
+
- `templates/hexi-fastapi-service` : FastAPI service starter with Hexi wiring
|
|
226
|
+
- `templates/hexi-typer-cli` : Typer CLI starter with Hexi wiring
|
|
227
|
+
- `templates/hexi-data-job` : data job starter with dry-run and Hexi wiring
|
|
228
|
+
- `templates/hexi-agent-worker` : embedded Hexi runtime starter
|
|
229
|
+
|
|
230
|
+
## Provenance
|
|
231
|
+
|
|
232
|
+
Made with ❤️ from 🇵🇪. El Perú es clave 🔑.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Hexi v0.1.0
|
|
2
|
+
|
|
3
|
+
Hexi is a minimal, contract-driven (hexagonal) coding-agent runtime and CLI.
|
|
4
|
+
It runs exactly one agent step per invocation against a local git repository.
|
|
5
|
+
|
|
6
|
+
## Test Drive (5 Minutes)
|
|
7
|
+
Run this in any local git repo you can safely modify.
|
|
8
|
+
|
|
9
|
+
1. Install Hexi:
|
|
10
|
+
```bash
|
|
11
|
+
pip install -e .
|
|
12
|
+
```
|
|
13
|
+
Optional OpenRouter support:
|
|
14
|
+
```bash
|
|
15
|
+
pip install -e ".[openrouter]"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
2. Initialize Hexi files:
|
|
19
|
+
```bash
|
|
20
|
+
hexi init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
3. Onboard provider/model and key:
|
|
24
|
+
```bash
|
|
25
|
+
hexi onboard
|
|
26
|
+
```
|
|
27
|
+
When prompted, pick any provider. For OpenRouter providers, install the optional extra and provide `OPENROUTER_API_KEY`.
|
|
28
|
+
|
|
29
|
+
4. Verify setup:
|
|
30
|
+
```bash
|
|
31
|
+
hexi doctor
|
|
32
|
+
```
|
|
33
|
+
Expected: provider/model printed and `Doctor check passed`.
|
|
34
|
+
|
|
35
|
+
5. Run one agent step:
|
|
36
|
+
```bash
|
|
37
|
+
hexi run "Add one tiny test for an existing function and run pytest"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
6. Inspect what changed:
|
|
41
|
+
```bash
|
|
42
|
+
hexi diff
|
|
43
|
+
tail -n 20 .hexi/runlog.jsonl
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
7. If you want to switch providers later:
|
|
47
|
+
```bash
|
|
48
|
+
hexi onboard
|
|
49
|
+
```
|
|
50
|
+
Re-run onboarding to update `.hexi/local.toml`.
|
|
51
|
+
|
|
52
|
+
## What it is
|
|
53
|
+
- Python package (PyPI distribution): `hexicodes`
|
|
54
|
+
- Core contracts in `hexi.core`
|
|
55
|
+
- Side-effect adapters in `hexi.adapters`
|
|
56
|
+
- One-step execution with structured event logging to `.hexi/runlog.jsonl`
|
|
57
|
+
|
|
58
|
+
## What it is not
|
|
59
|
+
- No daemon, no background workers, no web UI
|
|
60
|
+
- No MCP server and no SQLite in v0.1.0
|
|
61
|
+
- No multi-agent orchestration
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
```bash
|
|
66
|
+
pip install -e .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### OpenRouter support (optional)
|
|
70
|
+
- HTTP adapter only (`openrouter_http` provider):
|
|
71
|
+
```bash
|
|
72
|
+
pip install -e ".[openrouter-http]"
|
|
73
|
+
```
|
|
74
|
+
- SDK adapter only (`openrouter_sdk` provider):
|
|
75
|
+
```bash
|
|
76
|
+
pip install -e ".[openrouter-sdk]"
|
|
77
|
+
```
|
|
78
|
+
- Both OpenRouter adapters:
|
|
79
|
+
```bash
|
|
80
|
+
pip install -e ".[openrouter]"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Dev/test dependencies:
|
|
84
|
+
```bash
|
|
85
|
+
pip install -e ".[dev]"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## CLI
|
|
89
|
+
- `hexi --help` or `hexi help` : show command help
|
|
90
|
+
- `hexi --version` or `hexi version` : print installed version
|
|
91
|
+
- `hexi init` : create `.hexi/config.toml`, `.hexi/local.toml`, `.hexi/runlog.jsonl`
|
|
92
|
+
- `hexi onboard` : interactive setup for provider/model and optional local key paste
|
|
93
|
+
- `hexi new` : scaffold a project from built-in Hexi templates (non-interactive by default)
|
|
94
|
+
- `hexi demo` : fancy interactive flow with random/model-generated ideas and template scaffolding
|
|
95
|
+
- `hexi run "<task>"` : execute one agent step and emit structured events
|
|
96
|
+
- `hexi diff` : show current git diff
|
|
97
|
+
- `hexi doctor` : verbose diagnostics; use `--probe-model` for live “What model are you?” check
|
|
98
|
+
- `hexi plan-check --file plan.json` : validate/troubleshoot ActionPlan JSON directly
|
|
99
|
+
|
|
100
|
+
## Documentation (MkDocs + Read the Docs)
|
|
101
|
+
Build docs locally:
|
|
102
|
+
```bash
|
|
103
|
+
pip install -e ".[docs]"
|
|
104
|
+
mkdocs serve
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Read the Docs config is in `.readthedocs.yml`.
|
|
108
|
+
|
|
109
|
+
## Configuration design choices
|
|
110
|
+
Hexi uses layered TOML configuration:
|
|
111
|
+
1. `.hexi/config.toml` (repo defaults)
|
|
112
|
+
2. `.hexi/local.toml` (local machine overrides)
|
|
113
|
+
3. Environment variables (recommended for secrets)
|
|
114
|
+
|
|
115
|
+
For secrets, env vars are preferred. `hexi onboard` can write keys to `.hexi/local.toml` for local/testing convenience.
|
|
116
|
+
|
|
117
|
+
## Config shape (`.hexi/config.toml`)
|
|
118
|
+
```toml
|
|
119
|
+
[model]
|
|
120
|
+
provider = "openai_compat" # openrouter_http | openrouter_sdk | openai_compat | anthropic_compat
|
|
121
|
+
model = "gpt-4o-mini"
|
|
122
|
+
|
|
123
|
+
[providers.openrouter_http]
|
|
124
|
+
base_url = "https://openrouter.ai/api/v1"
|
|
125
|
+
api_style = "openai" # openai | anthropic
|
|
126
|
+
|
|
127
|
+
[providers.openrouter_sdk]
|
|
128
|
+
base_url = "https://openrouter.ai/api/v1"
|
|
129
|
+
|
|
130
|
+
[providers.openai_compat]
|
|
131
|
+
base_url = "https://api.openai.com/v1"
|
|
132
|
+
|
|
133
|
+
[providers.anthropic_compat]
|
|
134
|
+
base_url = "https://api.anthropic.com"
|
|
135
|
+
|
|
136
|
+
[policy]
|
|
137
|
+
allow_commands = ["git status", "git diff", "pytest", "python -m pytest"]
|
|
138
|
+
max_diff_chars = 4000
|
|
139
|
+
max_file_read_chars = 4000
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Local override example (`.hexi/local.toml`)
|
|
143
|
+
```toml
|
|
144
|
+
[model]
|
|
145
|
+
provider = "openrouter_http"
|
|
146
|
+
model = "anthropic/claude-sonnet-4-6"
|
|
147
|
+
|
|
148
|
+
[providers.openrouter_http]
|
|
149
|
+
api_style = "anthropic"
|
|
150
|
+
|
|
151
|
+
[secrets]
|
|
152
|
+
openrouter_api_key = "..."
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Env vars
|
|
156
|
+
- `OPENROUTER_API_KEY` for `openrouter_http` and `openrouter_sdk`
|
|
157
|
+
- `OPENAI_API_KEY` for `openai_compat`
|
|
158
|
+
- `ANTHROPIC_API_KEY` for `anthropic_compat`
|
|
159
|
+
|
|
160
|
+
## Packaging
|
|
161
|
+
- Distribution name: `hexicodes`
|
|
162
|
+
- Console script: `hexi`
|
|
163
|
+
- Optional extras:
|
|
164
|
+
- `openrouter-http`
|
|
165
|
+
- `openrouter-sdk`
|
|
166
|
+
- `openrouter`
|
|
167
|
+
- `docs`
|
|
168
|
+
- `dev`
|
|
169
|
+
|
|
170
|
+
## Included example projects
|
|
171
|
+
- `examples/todo_refiner` : minimal CLI-wrapper agent integration
|
|
172
|
+
- `examples/embedded_step` : direct embedded `RunStepService` usage
|
|
173
|
+
- `examples/policy_loop` : multi-step user-gated loop using repeated `hexi run`
|
|
174
|
+
|
|
175
|
+
## Included Hexi-native templates
|
|
176
|
+
- `templates/hexi-python-lib` : tested library starter with Hexi wiring
|
|
177
|
+
- `templates/hexi-fastapi-service` : FastAPI service starter with Hexi wiring
|
|
178
|
+
- `templates/hexi-typer-cli` : Typer CLI starter with Hexi wiring
|
|
179
|
+
- `templates/hexi-data-job` : data job starter with dry-run and Hexi wiring
|
|
180
|
+
- `templates/hexi-agent-worker` : embedded Hexi runtime starter
|
|
181
|
+
|
|
182
|
+
## Provenance
|
|
183
|
+
|
|
184
|
+
Made with ❤️ from 🇵🇪. El Perú es clave 🔑.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hexicodes"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Minimal contract-driven coding-agent runtime + CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Antonio Ognio" }]
|
|
13
|
+
maintainers = [{ name = "Antonio Ognio" }]
|
|
14
|
+
keywords = [
|
|
15
|
+
"ai",
|
|
16
|
+
"agent",
|
|
17
|
+
"coding-agent",
|
|
18
|
+
"cli",
|
|
19
|
+
"developer-tools",
|
|
20
|
+
"hexagonal-architecture",
|
|
21
|
+
"openrouter",
|
|
22
|
+
"typer"
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"License :: OSI Approved :: MIT License",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Topic :: Software Development :: Build Tools",
|
|
34
|
+
"Topic :: Software Development :: Libraries",
|
|
35
|
+
"Topic :: Software Development :: Testing"
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"typer>=0.12,<1",
|
|
39
|
+
"httpx>=0.27,<1",
|
|
40
|
+
"rich>=13.7,<15",
|
|
41
|
+
"tomli>=2.0.1; python_version < '3.11'"
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
openrouter-http = ["requests>=2.31,<3"]
|
|
46
|
+
openrouter-sdk = ["openrouter>=0.6.0,<1"]
|
|
47
|
+
openrouter = [
|
|
48
|
+
"requests>=2.31,<3",
|
|
49
|
+
"openrouter>=0.6.0,<1"
|
|
50
|
+
]
|
|
51
|
+
docs = [
|
|
52
|
+
"mkdocs>=1.6,<2",
|
|
53
|
+
"mkdocs-material>=9.5,<10"
|
|
54
|
+
]
|
|
55
|
+
dev = [
|
|
56
|
+
"pytest>=8.2,<9",
|
|
57
|
+
"pytest-asyncio>=0.23,<1",
|
|
58
|
+
"requests>=2.31,<3",
|
|
59
|
+
"responses>=0.25,<1",
|
|
60
|
+
"respx>=0.21,<1"
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[project.urls]
|
|
64
|
+
Homepage = "https://hexi.readthedocs.io"
|
|
65
|
+
Documentation = "https://hexi.readthedocs.io"
|
|
66
|
+
Source = "https://github.com/antonioognio/hexi"
|
|
67
|
+
Issues = "https://github.com/antonioognio/hexi/issues"
|
|
68
|
+
|
|
69
|
+
[project.scripts]
|
|
70
|
+
hexi = "hexi.cli:app"
|
|
71
|
+
|
|
72
|
+
[tool.setuptools]
|
|
73
|
+
package-dir = {"" = "src"}
|
|
74
|
+
license-files = ["LICENSE"]
|
|
75
|
+
|
|
76
|
+
[tool.setuptools.packages.find]
|
|
77
|
+
where = ["src"]
|
|
78
|
+
|
|
79
|
+
[tool.pytest.ini_options]
|
|
80
|
+
testpaths = ["tests"]
|
|
81
|
+
addopts = "-q"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .event_log_jsonl import JsonlRunlogEventSink
|
|
2
|
+
from .events_console import ConsoleEventSink
|
|
3
|
+
from .exec_local import LocalExec
|
|
4
|
+
from .memory_file import FileMemory
|
|
5
|
+
from .model_anthropic_compat import AnthropicCompatModel
|
|
6
|
+
from .model_openai_compat import OpenAICompatModel
|
|
7
|
+
from .model_openrouter_http import OpenRouterHTTPModel
|
|
8
|
+
from .model_openrouter_sdk import OpenRouterSDKModel
|
|
9
|
+
from .workspace_local_git import LocalGitWorkspace
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"JsonlRunlogEventSink",
|
|
13
|
+
"ConsoleEventSink",
|
|
14
|
+
"LocalExec",
|
|
15
|
+
"FileMemory",
|
|
16
|
+
"AnthropicCompatModel",
|
|
17
|
+
"OpenAICompatModel",
|
|
18
|
+
"OpenRouterHTTPModel",
|
|
19
|
+
"OpenRouterSDKModel",
|
|
20
|
+
"LocalGitWorkspace",
|
|
21
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from hexi.core.domain import Event
|
|
7
|
+
from hexi.core.schemas import event_to_dict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class JsonlRunlogEventSink:
|
|
11
|
+
def __init__(self, runlog_path: Path) -> None:
|
|
12
|
+
self.runlog_path = runlog_path
|
|
13
|
+
|
|
14
|
+
def emit(self, event: Event) -> None:
|
|
15
|
+
self.runlog_path.parent.mkdir(parents=True, exist_ok=True)
|
|
16
|
+
with self.runlog_path.open("a", encoding="utf-8") as f:
|
|
17
|
+
f.write(json.dumps(event_to_dict(event), ensure_ascii=True) + "\n")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from rich.console import Console, Group
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.syntax import Syntax
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
10
|
+
from hexi.core.domain import Event
|
|
11
|
+
|
|
12
|
+
_EVENT_STYLES: dict[str, tuple[str, str]] = {
|
|
13
|
+
"progress": ("cyan", "⏳"),
|
|
14
|
+
"question": ("yellow", "❓"),
|
|
15
|
+
"review": ("magenta", "🔎"),
|
|
16
|
+
"artifact": ("green", "📦"),
|
|
17
|
+
"error": ("red", "✖"),
|
|
18
|
+
"done": ("bright_blue", "✔"),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConsoleEventSink:
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
self.console = Console()
|
|
25
|
+
|
|
26
|
+
def emit(self, event: Event) -> None:
|
|
27
|
+
color, icon = _EVENT_STYLES.get(event.type, ("white", "•"))
|
|
28
|
+
title = Text(f"{icon} {event.type.upper()}", style=f"bold {color}")
|
|
29
|
+
subtitle = "blocking" if event.blocking else "non-blocking"
|
|
30
|
+
|
|
31
|
+
lines = [Text(event.one_line_summary, style="bold")]
|
|
32
|
+
if event.payload:
|
|
33
|
+
payload_str = json.dumps(event.payload, ensure_ascii=True, indent=2)
|
|
34
|
+
lines.append(Text(""))
|
|
35
|
+
lines.append(Syntax(payload_str, "json", word_wrap=True))
|
|
36
|
+
|
|
37
|
+
self.console.print(Panel.fit(Group(*lines), title=title, subtitle=subtitle, border_style=color))
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
from hexi.core.domain import Policy
|
|
7
|
+
from hexi.core.policy import command_allowed
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LocalExec:
|
|
11
|
+
def run(self, command: str, policy: Policy) -> tuple[int, str, str]:
|
|
12
|
+
if not command_allowed(command, policy):
|
|
13
|
+
raise PermissionError(f"command is not allowlisted: {command}")
|
|
14
|
+
args = shlex.split(command)
|
|
15
|
+
proc = subprocess.run(args, capture_output=True, text=True, check=False)
|
|
16
|
+
return proc.returncode, proc.stdout[-8000:], proc.stderr[-8000:]
|