curupira 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.
- curupira-0.1.0/.gitignore +190 -0
- curupira-0.1.0/CHANGELOG.md +48 -0
- curupira-0.1.0/CONTRIBUTING.md +221 -0
- curupira-0.1.0/LICENSE +21 -0
- curupira-0.1.0/PKG-INFO +324 -0
- curupira-0.1.0/README.md +290 -0
- curupira-0.1.0/SECURITY.md +27 -0
- curupira-0.1.0/crates/curupira-core/Cargo.lock +132 -0
- curupira-0.1.0/crates/curupira-core/Cargo.toml +14 -0
- curupira-0.1.0/crates/curupira-core/src/lib.rs +15 -0
- curupira-0.1.0/curupira.example.toml +92 -0
- curupira-0.1.0/hatch_build.py +100 -0
- curupira-0.1.0/pyproject.toml +142 -0
- curupira-0.1.0/src/curupira/__init__.py +5 -0
- curupira-0.1.0/src/curupira/__main__.py +5 -0
- curupira-0.1.0/src/curupira/_native.pyi +4 -0
- curupira-0.1.0/src/curupira/_version.py +3 -0
- curupira-0.1.0/src/curupira/agents/__init__.py +34 -0
- curupira-0.1.0/src/curupira/agents/base.py +112 -0
- curupira-0.1.0/src/curupira/agents/claude.py +30 -0
- curupira-0.1.0/src/curupira/agents/codex.py +40 -0
- curupira-0.1.0/src/curupira/agents/cursor.py +29 -0
- curupira-0.1.0/src/curupira/agents/opencode.py +30 -0
- curupira-0.1.0/src/curupira/cli.py +260 -0
- curupira-0.1.0/src/curupira/clients/__init__.py +1 -0
- curupira-0.1.0/src/curupira/clients/gh.py +198 -0
- curupira-0.1.0/src/curupira/clients/process.py +133 -0
- curupira-0.1.0/src/curupira/config.py +114 -0
- curupira-0.1.0/src/curupira/dispatcher.py +69 -0
- curupira-0.1.0/src/curupira/errors.py +68 -0
- curupira-0.1.0/src/curupira/executor.py +181 -0
- curupira-0.1.0/src/curupira/models/__init__.py +71 -0
- curupira-0.1.0/src/curupira/models/base.py +15 -0
- curupira-0.1.0/src/curupira/models/configuration.py +285 -0
- curupira-0.1.0/src/curupira/models/github.py +47 -0
- curupira-0.1.0/src/curupira/models/process.py +47 -0
- curupira-0.1.0/src/curupira/models/profiles.py +107 -0
- curupira-0.1.0/src/curupira/models/tasks.py +87 -0
- curupira-0.1.0/src/curupira/native.py +15 -0
- curupira-0.1.0/src/curupira/py.typed +0 -0
- curupira-0.1.0/src/curupira/runtime.py +117 -0
- curupira-0.1.0/src/curupira/scheduler.py +156 -0
- curupira-0.1.0/src/curupira/status.py +53 -0
- curupira-0.1.0/src/curupira/storage/__init__.py +6 -0
- curupira-0.1.0/src/curupira/storage/cron.py +119 -0
- curupira-0.1.0/src/curupira/storage/key_value.py +203 -0
- curupira-0.1.0/src/curupira/storage/sessions.py +29 -0
- curupira-0.1.0/src/curupira/tasks/__init__.py +5 -0
- curupira-0.1.0/src/curupira/tasks/base.py +62 -0
- curupira-0.1.0/src/curupira/tasks/cron.py +144 -0
- curupira-0.1.0/src/curupira/tasks/feed.py +103 -0
- curupira-0.1.0/src/curupira/tasks/github_issues.py +79 -0
- curupira-0.1.0/src/curupira/tasks/github_pull_requests.py +100 -0
- curupira-0.1.0/src/curupira/tasks/registry.py +33 -0
- curupira-0.1.0/src/curupira/telemetry.py +83 -0
- curupira-0.1.0/src/curupira/vcs/__init__.py +5 -0
- curupira-0.1.0/src/curupira/vcs/base.py +200 -0
- curupira-0.1.0/src/curupira/vcs/github_cli.py +19 -0
- curupira-0.1.0/src/gh_dispatch/tasks/__init__.py +1 -0
- curupira-0.1.0/tests/__init__.py +1 -0
- curupira-0.1.0/tests/agents/__init__.py +1 -0
- curupira-0.1.0/tests/agents/test_base.py +95 -0
- curupira-0.1.0/tests/agents/test_claude.py +70 -0
- curupira-0.1.0/tests/agents/test_codex.py +60 -0
- curupira-0.1.0/tests/agents/test_cursor.py +93 -0
- curupira-0.1.0/tests/agents/test_opencode.py +46 -0
- curupira-0.1.0/tests/fakes.py +137 -0
- curupira-0.1.0/tests/helpers.py +55 -0
- curupira-0.1.0/tests/tasks/__init__.py +1 -0
- curupira-0.1.0/tests/tasks/test_base.py +66 -0
- curupira-0.1.0/tests/tasks/test_cron.py +91 -0
- curupira-0.1.0/tests/tasks/test_cron_trigger.py +34 -0
- curupira-0.1.0/tests/tasks/test_feed.py +122 -0
- curupira-0.1.0/tests/tasks/test_github_issues.py +87 -0
- curupira-0.1.0/tests/tasks/test_github_pull_requests.py +106 -0
- curupira-0.1.0/tests/tasks/test_registry.py +79 -0
- curupira-0.1.0/tests/test_cli.py +157 -0
- curupira-0.1.0/tests/test_clients.py +303 -0
- curupira-0.1.0/tests/test_config.py +277 -0
- curupira-0.1.0/tests/test_dispatcher.py +299 -0
- curupira-0.1.0/tests/test_native.py +17 -0
- curupira-0.1.0/tests/test_process.py +123 -0
- curupira-0.1.0/tests/test_runtime.py +76 -0
- curupira-0.1.0/tests/test_scheduler.py +118 -0
- curupira-0.1.0/tests/test_session_repository.py +61 -0
- curupira-0.1.0/tests/test_status.py +57 -0
- curupira-0.1.0/tests/test_tasks.py +69 -0
- curupira-0.1.0/tests/test_telemetry.py +179 -0
- curupira-0.1.0/tests/test_verify_dist_version.py +112 -0
- curupira-0.1.0/tests/vcs/__init__.py +1 -0
- curupira-0.1.0/tests/vcs/test_base.py +150 -0
- curupira-0.1.0/uv.lock +2243 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Local curupira configuration and runtime state
|
|
40
|
+
/*-dispatch.toml
|
|
41
|
+
*.sqlite3
|
|
42
|
+
*.sqlite3-journal
|
|
43
|
+
workspaces/
|
|
44
|
+
|
|
45
|
+
# Unit test / coverage reports
|
|
46
|
+
htmlcov/
|
|
47
|
+
.tox/
|
|
48
|
+
.nox/
|
|
49
|
+
.coverage
|
|
50
|
+
.coverage.*
|
|
51
|
+
.cache
|
|
52
|
+
nosetests.xml
|
|
53
|
+
coverage.xml
|
|
54
|
+
*.cover
|
|
55
|
+
*.py,cover
|
|
56
|
+
.hypothesis/
|
|
57
|
+
.pytest_cache/
|
|
58
|
+
cover/
|
|
59
|
+
|
|
60
|
+
# Translations
|
|
61
|
+
*.mo
|
|
62
|
+
*.pot
|
|
63
|
+
|
|
64
|
+
# Django stuff:
|
|
65
|
+
*.log
|
|
66
|
+
local_settings.py
|
|
67
|
+
db.sqlite3
|
|
68
|
+
db.sqlite3-journal
|
|
69
|
+
|
|
70
|
+
# Flask stuff:
|
|
71
|
+
instance/
|
|
72
|
+
.webassets-cache
|
|
73
|
+
|
|
74
|
+
# Scrapy stuff:
|
|
75
|
+
.scrapy
|
|
76
|
+
|
|
77
|
+
# Sphinx documentation
|
|
78
|
+
docs/_build/
|
|
79
|
+
|
|
80
|
+
# PyBuilder
|
|
81
|
+
.pybuilder/
|
|
82
|
+
target/
|
|
83
|
+
|
|
84
|
+
# Jupyter Notebook
|
|
85
|
+
.ipynb_checkpoints
|
|
86
|
+
|
|
87
|
+
# IPython
|
|
88
|
+
profile_default/
|
|
89
|
+
ipython_config.py
|
|
90
|
+
|
|
91
|
+
# pyenv
|
|
92
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
93
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
94
|
+
# .python-version
|
|
95
|
+
|
|
96
|
+
# pipenv
|
|
97
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
98
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
99
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
100
|
+
# install all needed dependencies.
|
|
101
|
+
#Pipfile.lock
|
|
102
|
+
|
|
103
|
+
# UV
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
#uv.lock
|
|
108
|
+
|
|
109
|
+
# poetry
|
|
110
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
111
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
112
|
+
# commonly ignored for libraries.
|
|
113
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
114
|
+
#poetry.lock
|
|
115
|
+
|
|
116
|
+
# pdm
|
|
117
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
118
|
+
#pdm.lock
|
|
119
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
120
|
+
# in version control.
|
|
121
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
122
|
+
.pdm.toml
|
|
123
|
+
.pdm-python
|
|
124
|
+
.pdm-build/
|
|
125
|
+
|
|
126
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
127
|
+
__pypackages__/
|
|
128
|
+
|
|
129
|
+
# Celery stuff
|
|
130
|
+
celerybeat-schedule
|
|
131
|
+
celerybeat.pid
|
|
132
|
+
|
|
133
|
+
# SageMath parsed files
|
|
134
|
+
*.sage.py
|
|
135
|
+
|
|
136
|
+
# Environments
|
|
137
|
+
.env
|
|
138
|
+
.venv
|
|
139
|
+
env/
|
|
140
|
+
venv/
|
|
141
|
+
ENV/
|
|
142
|
+
env.bak/
|
|
143
|
+
venv.bak/
|
|
144
|
+
|
|
145
|
+
# Spyder project settings
|
|
146
|
+
.spyderproject
|
|
147
|
+
.spyproject
|
|
148
|
+
|
|
149
|
+
# Rope project settings
|
|
150
|
+
.ropeproject
|
|
151
|
+
|
|
152
|
+
# mkdocs documentation
|
|
153
|
+
/site
|
|
154
|
+
|
|
155
|
+
# mypy
|
|
156
|
+
.mypy_cache/
|
|
157
|
+
.dmypy.json
|
|
158
|
+
dmypy.json
|
|
159
|
+
|
|
160
|
+
# Pyre type checker
|
|
161
|
+
.pyre/
|
|
162
|
+
|
|
163
|
+
# pytype static type analyzer
|
|
164
|
+
.pytype/
|
|
165
|
+
|
|
166
|
+
# Cython debug symbols
|
|
167
|
+
cython_debug/
|
|
168
|
+
|
|
169
|
+
# PyCharm
|
|
170
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
171
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
172
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
173
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
174
|
+
#.idea/
|
|
175
|
+
|
|
176
|
+
# Ruff stuff:
|
|
177
|
+
.ruff_cache/
|
|
178
|
+
|
|
179
|
+
# Pyrefly stuff:
|
|
180
|
+
.pyrefly/
|
|
181
|
+
|
|
182
|
+
# PyPI configuration file
|
|
183
|
+
.pypirc
|
|
184
|
+
|
|
185
|
+
# Cursor
|
|
186
|
+
# Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to
|
|
187
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
188
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
189
|
+
.cursorignore
|
|
190
|
+
.cursorindexingignore
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] - 2026-10-07
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Rust crate `crates/curupira-core`, compiled with maturin into the `curupira._native`
|
|
11
|
+
extension shipped in the wheel. `rust_core_version()` returns the crate version.
|
|
12
|
+
- Keyed automations with a `trigger_type` discriminator (`issue`, `pull_request`,
|
|
13
|
+
`cron`) sharing one discovery, scheduling, and execution pipeline.
|
|
14
|
+
- Native provider adapters for OpenCode, Codex, Claude Code, and Cursor with
|
|
15
|
+
optional `model`/`effort`/`agent` translation and explicit permission options.
|
|
16
|
+
- Bounded async process runner with disconnected stdin, output limits, timeouts,
|
|
17
|
+
and process-group cleanup.
|
|
18
|
+
- Non-destructive SQLite state: incompatible files raise instead of being deleted.
|
|
19
|
+
- `validate`, `run` (with `--dry-run`), and `watch` commands with documented exit
|
|
20
|
+
codes.
|
|
21
|
+
- Local dispatch for GitHub issues, pull requests, and cron tasks, with isolated
|
|
22
|
+
task worktrees and persistent SQLite scheduling state.
|
|
23
|
+
- User and contributor guides at <https://caipora-labs.github.io/curupira/>.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Development tags `vX.Y.Z.devN` publish that PEP 440 version to PyPI through
|
|
28
|
+
`publish.yml` (environment `pypi`) and do not open a GitHub Release. Stable
|
|
29
|
+
`vX.Y.Z` tags publish to PyPI and open a GitHub Release.
|
|
30
|
+
- Renamed the installable package, primary console script, and Python import from `curupi`
|
|
31
|
+
to `curupira`, and added the short CLI alias `curu`. The product name remains OpsCli.
|
|
32
|
+
Configuration and state now default to `~/.curupira` (example file
|
|
33
|
+
`curupira.example.toml`, log `logs/curupira.log`). Task worktree branches use the
|
|
34
|
+
`curupira/` prefix. The native crate is `crates/curupira-core`, imported as
|
|
35
|
+
`curupira._native`.
|
|
36
|
+
- Renamed the installable package, console script, and Python import from `opscli`
|
|
37
|
+
to `curupi`. The product name remains OpsCli. Configuration and state now default
|
|
38
|
+
to `~/.curupi` (example file `curupi.example.toml`, log `logs/curupi.log`). Task
|
|
39
|
+
worktree branches use the `curupi/` prefix. The native crate is `crates/curupi-core`,
|
|
40
|
+
imported as `curupi._native`.
|
|
41
|
+
- Renamed the product to OpsCli, the package and executable to `opscli`, and the
|
|
42
|
+
per-user runtime directory to `~/.opscli`.
|
|
43
|
+
- Configuration moved to `settings` plus `coding_agents` with global polling and
|
|
44
|
+
per-automation prompts; the map key is the automation ID.
|
|
45
|
+
- Custom-agent names are only accepted where a verified native flag exists
|
|
46
|
+
(`--agent` for OpenCode and Claude Code).
|
|
47
|
+
|
|
48
|
+
## [Unreleased]
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
**OpsCli** (by Caipora Labs) is the product name. The installable package, primary console script, and Python import are `curupira`. The short CLI alias is `curu`.
|
|
4
|
+
|
|
5
|
+
## Environment setup
|
|
6
|
+
|
|
7
|
+
Requirements: Python 3.11+, [`uv`](https://docs.astral.sh/uv/), `gh` for live
|
|
8
|
+
discovery (tests use fakes and need no authentication), and a stable [Rust](https://rustup.rs/)
|
|
9
|
+
toolchain when building the native extension or a wheel.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv sync --dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`uv sync` installs the Python package and the `curupira` and `curu` console scripts. It does not
|
|
16
|
+
compile Rust, so the CLI and the Python test suite run without a toolchain.
|
|
17
|
+
|
|
18
|
+
A fresh contributor verifies everything with the commands below. They must all pass
|
|
19
|
+
before opening a pull request; CI runs the same steps. `uv build` compiles the native
|
|
20
|
+
extension and therefore needs Rust on `PATH`.
|
|
21
|
+
|
|
22
|
+
## Verification commands
|
|
23
|
+
|
|
24
|
+
Install the documentation tools with `uv sync --group docs`, then preview the site with
|
|
25
|
+
`uv run mkdocs serve`.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv run --no-sync pytest
|
|
29
|
+
uv run --no-sync ruff check .
|
|
30
|
+
uv run --no-sync ruff format --check .
|
|
31
|
+
uv run --no-sync pyrefly check
|
|
32
|
+
uv build
|
|
33
|
+
uv run --no-sync twine check dist/*
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Native extension
|
|
37
|
+
|
|
38
|
+
The Rust crate is `crates/curupira-core`, matching the `curupira` distribution name.
|
|
39
|
+
Hatchling stays the PEP 517 backend, keeps the version in `src/curupira/_version.py`,
|
|
40
|
+
and keeps the `curupira` and `curu` script entry points. A wheel build hook runs maturin (PyO3)
|
|
41
|
+
and packs the compiled module as `curupira._native`.
|
|
42
|
+
Editable installs skip that compile.
|
|
43
|
+
|
|
44
|
+
Build the extension into the current environment:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv sync --dev
|
|
48
|
+
uv run maturin develop
|
|
49
|
+
python -c "from curupira._native import rust_core_version; print(rust_core_version())"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`maturin develop` warns that the build backend is Hatchling. That warning is expected:
|
|
53
|
+
Hatchling still packages the Python project and the `curupira` and `curu` scripts, and maturin only
|
|
54
|
+
compiles the extension.
|
|
55
|
+
|
|
56
|
+
`rust_core_version()` returns the `curupira-core` crate version. `curupira.native.rust_core_version`
|
|
57
|
+
is a thin wrapper around that function. `tests/test_native.py` runs the same check when
|
|
58
|
+
the extension is already built and skips otherwise.
|
|
59
|
+
|
|
60
|
+
Build a wheel that includes the extension (this is what CI and `pip install` use):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uv build
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
When collecting coverage, it must stay at or above 85% branch coverage
|
|
67
|
+
(`fail_under = 85` in `pyproject.toml`). The example configuration is covered by tests:
|
|
68
|
+
changes to `curupira.example.toml` must keep `test_example_configuration_is_valid` green.
|
|
69
|
+
|
|
70
|
+
## Architecture boundaries
|
|
71
|
+
|
|
72
|
+
OpsCli separates task discovery, repository version control, and coding-agent CLI
|
|
73
|
+
invocation into three layers. Each layer owns a contract in its `base.py`:
|
|
74
|
+
|
|
75
|
+
- `src/curupira/tasks/` discovers work. `tasks/base.py` defines `TaskFeed` (polling
|
|
76
|
+
and streaming discovered tasks), `TaskSource` (discovering tasks for an automation),
|
|
77
|
+
`Trigger` (trigger-specific prompt data and feed construction), and
|
|
78
|
+
`FeedDependencies`. `tasks/feed.py` provides the reusable `PollingTaskFeed`, while
|
|
79
|
+
`tasks/registry.py` registers trigger types and aliases. Current sources/triggers
|
|
80
|
+
are implemented in `tasks/cron.py`, `tasks/github_issues.py`, and
|
|
81
|
+
`tasks/github_pull_requests.py`.
|
|
82
|
+
- `src/curupira/vcs/` prepares repositories. `vcs/base.py` defines `VersionControl`;
|
|
83
|
+
providers implement its `clone(repo, destination)` operation, while shared checkout,
|
|
84
|
+
worktree, and setup behavior stays in the base class. `vcs/github_cli.py` implements
|
|
85
|
+
cloning through the GitHub CLI.
|
|
86
|
+
- `src/curupira/agents/` invokes coding-agent CLIs. `agents/base.py` defines
|
|
87
|
+
`CodingAgentCliAdapter`; an adapter implements `build_arguments(request)` to map a
|
|
88
|
+
validated task request to that CLI's native arguments. The factory
|
|
89
|
+
`create_cli_adapter` in `agents/__init__.py` constructs supported adapters:
|
|
90
|
+
`opencode.py`, `codex.py`, `claude.py`, and `cursor.py`.
|
|
91
|
+
|
|
92
|
+
`src/curupira/storage/` is local SQLite persistence, not a version-control provider.
|
|
93
|
+
OpsCli does not manage authentication: provider CLIs and the user's environment provide
|
|
94
|
+
their own authentication.
|
|
95
|
+
|
|
96
|
+
To add a task source, implement `TaskSource`, provide a `Trigger`, and register its
|
|
97
|
+
`trigger_type` in `tasks/registry.py`; use a dedicated issue/PR after the task layer's
|
|
98
|
+
`base.py` contract. A new version-control provider implements `VersionControl.clone`
|
|
99
|
+
and belongs in its own issue/PR after `vcs/base.py`. A new coding-agent adapter
|
|
100
|
+
implements `CodingAgentCliAdapter.build_arguments` and is wired into
|
|
101
|
+
`create_cli_adapter`; it belongs in its own issue/PR after `agents/base.py`. Trello,
|
|
102
|
+
Azure DevOps, and Monday are examples of services where a future task source could
|
|
103
|
+
belong; they are not currently supported providers. Configuration accepts only the
|
|
104
|
+
trigger types and agent profiles defined by the current registry and models.
|
|
105
|
+
|
|
106
|
+
Cover new behavior with fakes in `tests/` — never start authenticated agents or hit the
|
|
107
|
+
network in tests.
|
|
108
|
+
|
|
109
|
+
When adding a provider or CLI, update `docs/data/requirements.toml` and the corresponding
|
|
110
|
+
adapter in `src/curupira/agents/`; the English installation requirements are rendered from
|
|
111
|
+
that TOML file during the MkDocs build.
|
|
112
|
+
|
|
113
|
+
## Documentation translations
|
|
114
|
+
|
|
115
|
+
Edit the canonical English pages in `docs/en/` first. When English documentation changes,
|
|
116
|
+
open a follow-up pull request to synchronize the corresponding Portuguese (`docs/pt/`) and
|
|
117
|
+
Spanish (`docs/es/`) pages. Generated Pydantic reference stays canonical in English; other
|
|
118
|
+
languages should link to it rather than manually translating generated fields.
|
|
119
|
+
|
|
120
|
+
## Documentation brand tokens
|
|
121
|
+
|
|
122
|
+
The docs theme uses the Caipora Labs palette. These hex values are the brand tokens.
|
|
123
|
+
Do not add other brand colors without a new decision. The Material overrides live in
|
|
124
|
+
`docs/stylesheets/extra.css`.
|
|
125
|
+
|
|
126
|
+
| Token | Hex | Role |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| `primary` | `#F7931F` | orange brand |
|
|
129
|
+
| `primary-deep` | `#EA6114` | contrast / CTAs |
|
|
130
|
+
| `skin` | `#8E4F26` | Caipora brown |
|
|
131
|
+
| `accent` | `#39873B` | leaf / success |
|
|
132
|
+
| `neutral-0` | `#FEFDFC` | background |
|
|
133
|
+
| `neutral-900` | `#1A1A1A` | text |
|
|
134
|
+
|
|
135
|
+
On the light scheme, the header uses `primary` with `neutral-900` text, and primary
|
|
136
|
+
buttons use `primary-deep`. Body links use `skin`, which stays readable on `neutral-0`.
|
|
137
|
+
The leaf `accent` is the hover color. The dark scheme swaps the neutrals and uses
|
|
138
|
+
`primary` for links. The optional OpsCli product accent (`#014FC9` / `#011E58`) is not
|
|
139
|
+
applied on the docs theme.
|
|
140
|
+
|
|
141
|
+
## Dependency audits
|
|
142
|
+
|
|
143
|
+
`pip-audit` runs in CI against the synced development environment:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
uv run pip-audit
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Investigate every finding: upgrade the affected constraint in `pyproject.toml`,
|
|
150
|
+
re-sync the lockfile, and re-run the full verification suite. If a finding is not
|
|
151
|
+
exploitable in this project (for example, a dev-only tool with no network path to
|
|
152
|
+
untrusted input), document the reason in the pull request instead of adding a
|
|
153
|
+
permanent ignore.
|
|
154
|
+
|
|
155
|
+
## Releases
|
|
156
|
+
|
|
157
|
+
Versioning is `MAJOR.MINOR.PATCH`. The single version source is
|
|
158
|
+
`src/curupira/_version.py`; the build backend reads it, and the CLI reports it.
|
|
159
|
+
Built wheels and sdists use that string as-is, so the Git tag and the file match
|
|
160
|
+
character for character after the tag's leading `v`.
|
|
161
|
+
|
|
162
|
+
### PyPI development rehearsal
|
|
163
|
+
|
|
164
|
+
Tag `vX.Y.Z.devN` publishes package `X.Y.Z.devN` (PEP 440) to PyPI. Bump the version
|
|
165
|
+
in `src/curupira/_version.py`, commit that change, and tag the same commit. Tag
|
|
166
|
+
`v0.1.0.dev0` already exists and must not be reused. The next rehearsal is
|
|
167
|
+
`0.1.0.dev1` with tag `v0.1.0.dev1`.
|
|
168
|
+
|
|
169
|
+
1. Set `__version__` to the next unused `X.Y.Z.devN` and commit.
|
|
170
|
+
2. Wait until CI is green on that commit. The tag workflow publishes only after the
|
|
171
|
+
Linux test matrix, lint, type check, and distribution build have succeeded for
|
|
172
|
+
the tagged SHA.
|
|
173
|
+
3. Tag that commit and push the tag:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
git tag v0.1.0.dev1
|
|
177
|
+
git push origin v0.1.0.dev1
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`publish.yml` builds the Rust-enabled wheel and sdist with `uv build`, checks that
|
|
181
|
+
the distribution version equals the tag without its leading `v`, and publishes with
|
|
182
|
+
Trusted Publishing (`id-token: write`, environment `pypi`). Use the canonical dotted
|
|
183
|
+
form `vX.Y.Z.devN`. PyPI keeps an uploaded file, so each rehearsal needs a new
|
|
184
|
+
suffix.
|
|
185
|
+
|
|
186
|
+
Tags that contain `.dev` do not open a GitHub Release (`release.yml` still skips
|
|
187
|
+
them). `testpypi.yml` is unchanged: the same `v*.dev*` tags, and a manual
|
|
188
|
+
`workflow_dispatch` from `main`, still target TestPyPI. The rehearsal that gates the
|
|
189
|
+
first stable publish is the real PyPI upload from `publish.yml`.
|
|
190
|
+
|
|
191
|
+
Stable `vX.Y.Z` tags keep the production path below. Issue #103 publishes `0.1.0`
|
|
192
|
+
from a commit whose `__version__` is `0.1.0`.
|
|
193
|
+
|
|
194
|
+
To cut a release:
|
|
195
|
+
|
|
196
|
+
1. Move the `Unreleased` entries in `CHANGELOG.md` into a new version section.
|
|
197
|
+
2. Bump `__version__` in `src/curupira/_version.py` to the stable `X.Y.Z` version.
|
|
198
|
+
3. Run the full verification suite and confirm `uv build` plus
|
|
199
|
+
`uv run --no-sync twine check dist/*` pass.
|
|
200
|
+
4. Rehearse with a `vX.Y.Z.devN` tag on PyPI (see above) before the first production
|
|
201
|
+
publication.
|
|
202
|
+
5. Tag the validated commit as `vX.Y.Z` and push the tag. The `publish.yml` workflow
|
|
203
|
+
publishes that version to PyPI; the `release.yml` workflow attaches the distributions
|
|
204
|
+
to the matching GitHub release.
|
|
205
|
+
|
|
206
|
+
PyPI publishing uses Trusted Publishing (OIDC), so no API tokens are stored. Before
|
|
207
|
+
publishing, a PyPI maintainer registers this repository as a trusted publisher for the
|
|
208
|
+
`curupira` project with GitHub owner `caipora-labs`, repository `curupira`, workflow
|
|
209
|
+
filename `publish.yml`, and environment `pypi`. Development tags `vX.Y.Z.devN` use
|
|
210
|
+
that same publisher. `testpypi.yml` remains a separate workflow with environment
|
|
211
|
+
`testpypi`, workflow filename `testpypi.yml`, and audience `testpypi`. It uploads to
|
|
212
|
+
`https://test.pypi.org/legacy/` and smoke-tests that installation. The publish
|
|
213
|
+
workflow checks that the full Linux test matrix, lint, type check, and distribution
|
|
214
|
+
build succeeded for the commit.
|
|
215
|
+
|
|
216
|
+
## Style
|
|
217
|
+
|
|
218
|
+
Ruff (lint + format) and strict Pyrefly govern style: typed signatures everywhere,
|
|
219
|
+
`@override` on overrides, docstrings on public modules/classes/methods, no blanket
|
|
220
|
+
suppressions. Test helpers may use `**overrides: Any`; keep other `Any` usage narrow
|
|
221
|
+
and justified.
|
curupira-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mario Taddeucci
|
|
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.
|