evateam-cli 0.1.0__py3-none-macosx_13_0_arm64.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.
@@ -0,0 +1,46 @@
1
+ """Launcher for the `eva` binary shipped in this wheel.
2
+
3
+ The binary is compiled Mojo. Its rpath finds Mojo's runtime libraries in the
4
+ mojo-compiler package next to this one in site-packages. For its HTTPS calls
5
+ it embeds a libpython, and this launcher points it at the interpreter that is
6
+ running here.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import sysconfig
12
+ from pathlib import Path
13
+
14
+
15
+ def _libpython() -> str | None:
16
+ libdir = sysconfig.get_config_var("LIBDIR") or ""
17
+ names = [
18
+ sysconfig.get_config_var("LDLIBRARY") or "",
19
+ sysconfig.get_config_var("INSTSONAME") or "",
20
+ f"libpython{sysconfig.get_python_version()}.dylib",
21
+ f"libpython{sysconfig.get_python_version()}.so.1.0",
22
+ f"libpython{sysconfig.get_python_version()}.so",
23
+ ]
24
+ for name in names:
25
+ if name and not name.endswith(".a"):
26
+ path = Path(libdir) / name
27
+ if path.exists():
28
+ return str(path)
29
+ framework = sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")
30
+ if framework:
31
+ path = Path(framework) / "Python.framework" / "Versions" / sysconfig.get_python_version() / "Python"
32
+ if path.exists():
33
+ return str(path)
34
+ return None
35
+
36
+
37
+ def main() -> None:
38
+ binary = Path(__file__).parent / "bin" / "eva"
39
+ env = dict(os.environ)
40
+ if "MOJO_PYTHON_LIBRARY" not in env:
41
+ lib = _libpython()
42
+ if lib:
43
+ env["MOJO_PYTHON_LIBRARY"] = lib
44
+ # From a venv the embedded interpreter cannot find its own prefix.
45
+ env.setdefault("PYTHONHOME", sys.base_prefix)
46
+ os.execve(binary, [str(binary), *sys.argv[1:]], env)
@@ -0,0 +1,3 @@
1
+ from evateam_cli import main
2
+
3
+ main()
evateam_cli/bin/eva ADDED
Binary file
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: evateam-cli
3
+ Version: 0.1.0
4
+ Summary: EvaTeam from the command line, written in Mojo
5
+ Author: Ilya Lubenets
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Project-URL: Source, https://github.com/ormeilu/evateam-cli
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: mojo-compiler==1.2.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # eva — EvaTeam from the command line, in Mojo
14
+
15
+ `eva` is a command-line client for [EvaTeam](https://www.evateam.ru/), the
16
+ Russian task tracker and wiki. It is a sibling of
17
+ [ytcli](https://github.com/ormeilu/yandex-tracker-cli), which does the same for
18
+ Yandex Tracker, and it is written in [Mojo](https://www.modular.com/mojo) for
19
+ fun.
20
+
21
+ Like ytcli, it has two readers in mind: a person at a terminal and an AI agent
22
+ behind a pipe. The terminal gets colour and full descriptions. The pipe gets
23
+ plain text, a capped description and a note saying what was left out.
24
+
25
+ ```console
26
+ $ eva task get DEV-1
27
+ DEV-1 Вложения теряются при переносе задачи
28
+ status: В работе stage: IN_PROGRESS
29
+ assignee: ilya@example.ru author: rep@example.ru
30
+ parent: DEV Development
31
+ created: 2026-11-01T10:00:00+03:00 updated: 2026-11-02T10:00:00+03:00
32
+ <untrusted src="DEV-1/text">
33
+ Шаги:
34
+
35
+ - Приложить файл
36
+ - Перенести
37
+ </untrusted>
38
+
39
+ $ eva task list -p DEV -s open -n 2
40
+ DEV-2 Открыто - Retry uploads on 5xx
41
+ DEV-7 Открыто ilya@example.ru Flaky upload test
42
+ shown 2 of 14 — next: --page 2
43
+ ```
44
+
45
+ Task descriptions and comments are written by other people, so `eva` fences
46
+ them: a `▏` margin in a terminal, an `<untrusted src="…">` tag in a pipe. An
47
+ agent reading the output can tell the tracker's data from instructions.
48
+
49
+ ## Install
50
+
51
+ eva is built for macOS on Apple silicon and Linux on x86_64 and arm64 (glibc
52
+ 2.34 or newer). Pick one way to install it.
53
+
54
+ With the install script. It installs [pixi](https://pixi.sh) if it is missing,
55
+ then eva from the conda channel `prefix.dev/ormeilu`. Run it again to update:
56
+
57
+ ```bash
58
+ curl -fsSL https://raw.githubusercontent.com/ormeilu/evateam-cli/main/install.sh | sh
59
+ ```
60
+
61
+ With pixi directly, which is what the script does:
62
+
63
+ ```bash
64
+ pixi global install -c https://prefix.dev/ormeilu -c https://conda.modular.com/max -c conda-forge evateam-cli
65
+ ```
66
+
67
+ From PyPI, if you prefer uv or pipx:
68
+
69
+ ```bash
70
+ uvx --from evateam-cli eva --help # run without installing
71
+ uv tool install evateam-cli # or put `eva` on PATH
72
+ pipx install evateam-cli
73
+ ```
74
+
75
+ `eva upgrade` updates eva the same way it was installed.
76
+
77
+ The two routes differ in size. Both carry Mojo's runtime libraries as a
78
+ dependency on Modular's `mojo-compiler` package. On PyPI that package is about
79
+ 70 MB. The conda package brings the whole compiler, and the pixi environment
80
+ ends up around 300 MB. The binary itself is under half a megabyte. Either way
81
+ eva makes its HTTPS calls through the Python it is installed with, so there is
82
+ nothing else to set up.
83
+
84
+ From source, with [pixi](https://pixi.sh):
85
+
86
+ ```bash
87
+ git clone https://github.com/ormeilu/evateam-cli
88
+ cd evateam-cli
89
+ pixi run build # → build/eva
90
+ ```
91
+
92
+ ## Configure
93
+
94
+ Create a token in EvaTeam: «Моя страница» → «Безопасность» → «Сгенерировать
95
+ API токен». Then set two variables, in the environment, in `./.env` or in
96
+ `~/.config/eva-cli/env`:
97
+
98
+ ```bash
99
+ EVA_URL=https://eva.example.ru
100
+ EVA_API_KEY=…
101
+ ```
102
+
103
+ `EVA_INSECURE=1` skips TLS verification, for self-hosted servers with their own
104
+ certificate. `eva config` shows what was picked up and from where, without
105
+ printing the token.
106
+
107
+ ## Commands
108
+
109
+ | Command | What it does |
110
+ |---|---|
111
+ | `eva task get CODE [--fields a,b]` | one task: status, people, parent, description |
112
+ | `eva task list [-p PROJ] [-a LOGIN] [-s STATUS] [-q TEXT]` | search; `-s` takes `open`, `closed`, `in_progress`, `in_review`, `all` or a status code |
113
+ | `eva task create -p PROJ -t TITLE [-d TEXT] [-a LOGIN] [--sprint SPR-1]` | new task |
114
+ | `eva task update CODE [-t] [-d] [-a] [--set JSON]` | change fields; `--set` passes any field through |
115
+ | `eva task statuses CODE` | the statuses the task's workflow lets it move to from here |
116
+ | `eva task move CODE STATUS [-m TEXT] [--set JSON]` | change status through a workflow transition; `--set` fills fields the transition requires |
117
+ | `eva task comment CODE TEXT` / `eva task comments CODE` | write or read comments |
118
+ | `eva task log CODE 1h30m [-m TEXT]` | log time spent |
119
+ | `eva task links CODE` | incoming and outgoing links |
120
+ | `eva project list`, `eva person list`, `eva doc list` | lists, with `-q` to search by name |
121
+ | `eva doc get CODE` | a wiki document as text |
122
+ | `eva call METHOD --kwargs JSON` | any API method; prints the raw reply |
123
+
124
+ Every list takes `-n/--limit` and `--page` and ends with `shown N of M`, so a
125
+ page is never mistaken for the whole answer. `--json` prints the API's own
126
+ JSON, `--full` turns off truncation in a pipe, and `-v` prints each request
127
+ and reply on stderr.
128
+
129
+ `--filter` on `task list` adds a raw BQL condition, for anything the flags do
130
+ not cover:
131
+
132
+ ```bash
133
+ eva task list --filter '["cmf_created_at", ">=", "2026-10-01"]'
134
+ ```
135
+
136
+ ## How it is built
137
+
138
+ EvaTeam speaks JSON-RPC 2.2: every call is a `POST /api/` with a method name
139
+ such as `CmfTask.list` and keyword arguments. The pieces:
140
+
141
+ - `src/eva/json.mojo`: a JSON parser and writer. Nodes sit in one arena and
142
+ refer to their children by index, which avoids a recursive type. Numbers keep
143
+ their source text, so ids and timestamps pass through unchanged.
144
+ - `src/eva/client.mojo`: configuration and the RPC call. Mojo's standard
145
+ library has no TLS yet, so the HTTPS request itself goes through Python's
146
+ `urllib` via Mojo's Python interop. Request building and reply parsing are
147
+ Mojo.
148
+ - `src/eva/render.mojo`: terminal and pipe output, HTML to text, colour via
149
+ [mist](https://github.com/thatstoasty/mist) from the
150
+ [modular-community](https://prefix.dev/channels/modular-community) channel.
151
+ - `src/eva/commands.mojo`: the command tree.
152
+
153
+ The JSON parser is home-made because the `emberjson` build in modular-community
154
+ was compiled for Mojo 1.1 and does not load under 1.2.
155
+
156
+ ## Develop
157
+
158
+ ```bash
159
+ pixi run test # unit tests: JSON, rendering, argument parsing
160
+ pixi run e2e # builds eva and runs it against tests/mock_server.py
161
+ ```
162
+
163
+ `pixi run conda-package` builds the conda package from `recipe/recipe.yaml`
164
+ into `output/`. `pixi run wheel macosx_13_0_arm64` packs `build/eva` into a wheel in `dist/`,
165
+ with its rpath pointed at `mojo-compiler`'s libraries in the same
166
+ site-packages. A tag `vX.Y.Z` matching `pixi.toml` runs
167
+ `.github/workflows/release.yml`: wheels and conda packages for three
168
+ platforms, the end-to-end suite against each one installed the way a user
169
+ would, then PyPI, prefix.dev and a GitHub release.
170
+
171
+ `tests/mock_server.py` is a small JSON-RPC server with two canned tasks. The
172
+ end-to-end script checks both what `eva` prints and what it sends.
173
+
174
+ ## Status
175
+
176
+ Checked against a live EvaTeam instance, reads and writes: every command in the
177
+ table above, paging and counts, and a test task taken from creation to Done.
178
+
179
+ Two things the docs do not say, found that way:
180
+
181
+ - Plain `CmfTask.update` with a new status is accepted and ignored. A status
182
+ change has to run a workflow transition (`CmfTask.task_transition_run`), which
183
+ is what `task move` does: it reads the task's workflow, finds the transition
184
+ from the current status and reports which targets exist when the one asked for
185
+ does not.
186
+ - A refused call comes back as HTTP 200 with an `abort` message ("Необходимо
187
+ заполнить Вид услуги") and no `error`. `eva` treats it as an error.
188
+
189
+ ## Licence
190
+
191
+ MIT
@@ -0,0 +1,8 @@
1
+ evateam_cli/__init__.py,sha256=2F7qCr62iEkmzTUakBosjg84MjY32oT7-Yl6zfmlCvA,1621
2
+ evateam_cli/__main__.py,sha256=q93TOIsNvR12G0fVRFOhytbgkRlp9yrULjSzADJkkjQ,37
3
+ evateam_cli/bin/eva,sha256=ZfGgnSoPxlNM9S7JxpEuhljK0_rNjxpG9vYplXItolo,471296
4
+ evateam_cli-0.1.0.dist-info/licenses/LICENSE,sha256=RooB9VjlLCKnSgdSsnReIFpZNaTLoFqdRdC1tjMPKlE,1070
5
+ evateam_cli-0.1.0.dist-info/METADATA,sha256=eBpUCb9J3-7EF00cZ__cdCWIgYadz55ZvW0zlDPOWS4,7732
6
+ evateam_cli-0.1.0.dist-info/WHEEL,sha256=jbuqoWejtGFyBB65Kb0ZrMoo8Gc6egtaqIHMy9AGS_A,112
7
+ evateam_cli-0.1.0.dist-info/entry_points.txt,sha256=UCDJ-pfUgLpIIImK-MvSyLU1T-sid9CMKC2pQ1MERuw,41
8
+ evateam_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: evateam-cli build_wheel.py
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-macosx_13_0_arm64
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ eva = evateam_cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ilya Lubenets
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.