gencli-agents 0.0.1__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.
- gencli_agents-0.0.1/LICENSE +21 -0
- gencli_agents-0.0.1/PKG-INFO +307 -0
- gencli_agents-0.0.1/README.md +278 -0
- gencli_agents-0.0.1/gencli/__init__.py +12 -0
- gencli_agents-0.0.1/gencli/commands/__init__.py +0 -0
- gencli_agents-0.0.1/gencli/commands/config.py +224 -0
- gencli_agents-0.0.1/gencli/commands/example.py +119 -0
- gencli_agents-0.0.1/gencli/constants.py +7 -0
- gencli_agents-0.0.1/gencli/main.py +29 -0
- gencli_agents-0.0.1/gencli/utils.py +8 -0
- gencli_agents-0.0.1/pyproject.toml +87 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gustavo Padilha Polleti
|
|
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.
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gencli-agents
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: General-purpose CLI: a library of functions exposed as commands, built for agents to use and extend
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: cli,typer,agents,tooling
|
|
8
|
+
Author: gustavo.polleti
|
|
9
|
+
Author-email: gustavo.polleti@gmail.com
|
|
10
|
+
Requires-Python: >=3.9,<4.0
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Development Status :: 4 - Beta
|
|
18
|
+
Classifier: Environment :: Console
|
|
19
|
+
Classifier: Intended Audience :: Developers
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Dist: rich (>=14.0.0,<15.0.0)
|
|
22
|
+
Requires-Dist: shellingham (>=1.5.0,<2.0.0)
|
|
23
|
+
Requires-Dist: typer (>=0.17.0,<0.18.0)
|
|
24
|
+
Project-URL: Documentation, https://gpadpoll.github.io/general-cli/
|
|
25
|
+
Project-URL: Homepage, https://github.com/gpadpoll/general-cli
|
|
26
|
+
Project-URL: Repository, https://github.com/gpadpoll/general-cli
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# gencli
|
|
30
|
+
|
|
31
|
+
A general-purpose CLI: a library of functions exposed as commands, built for
|
|
32
|
+
agents to use and extend. Every tool is implemented as a small set of pure
|
|
33
|
+
functions with a thin Typer command wrapper, so the same logic works both as
|
|
34
|
+
a shell command and as a plain Python import (from a script, a notebook, or
|
|
35
|
+
another agent's tool).
|
|
36
|
+
|
|
37
|
+
See [`AGENTS.md`](./AGENTS.md) for the SDLC every new tool must follow
|
|
38
|
+
(pure functions, tests, and a documentation notebook per feature).
|
|
39
|
+
|
|
40
|
+
## Get Started
|
|
41
|
+
|
|
42
|
+
See the Docs page: https://gpadpoll.github.io/general-cli/
|
|
43
|
+
|
|
44
|
+
## Important: Poetry Version
|
|
45
|
+
|
|
46
|
+
To avoid compatibility errors (such as TypeError related to canonicalize_version), ensure you are using an up-to-date version of Poetry:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install --upgrade poetry
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
If you encounter installation issues, upgrading Poetry usually resolves them.
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
1. **Environment**: create (or verify) the conda environment for this project
|
|
57
|
+
```bash
|
|
58
|
+
make conda-env
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
2. **Installation**: install the package in development mode
|
|
62
|
+
```bash
|
|
63
|
+
make install
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
3. **Basic Usage**: try the built-in commands
|
|
67
|
+
```bash
|
|
68
|
+
gencli config list
|
|
69
|
+
gencli config set theme dark
|
|
70
|
+
gencli config get theme
|
|
71
|
+
|
|
72
|
+
gencli example slug "Hello, World!"
|
|
73
|
+
gencli example word-count "to be or not to be"
|
|
74
|
+
gencli example reverse "the quick brown fox"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## CLI Commands
|
|
78
|
+
|
|
79
|
+
- `gencli config` — manage CLI configuration (`set`, `get`, `list`, `reset`),
|
|
80
|
+
stored as JSON at `~/.gencli_config.json` (override with
|
|
81
|
+
`GENCLI_CONFIG_PATH`).
|
|
82
|
+
- `gencli example` — reference tool (`slug`, `word-count`, `reverse`). This
|
|
83
|
+
is the template to copy when adding a new tool: see
|
|
84
|
+
`gencli/commands/example.py`, its tests in `tests/test_example.py`, and
|
|
85
|
+
its notebook in `docs/notebooks/example.ipynb`.
|
|
86
|
+
|
|
87
|
+
## Adding a new tool
|
|
88
|
+
|
|
89
|
+
1. Create `gencli/commands/<tool>.py`. Implement the tool's logic as pure
|
|
90
|
+
functions (no I/O, no side effects, deterministic), and thin Typer
|
|
91
|
+
commands that call them.
|
|
92
|
+
2. Register the sub-app in `gencli/main.py`.
|
|
93
|
+
3. Write unit tests in `tests/test_<tool>.py`: direct tests for the pure
|
|
94
|
+
functions, plus a couple of `CliRunner` tests for the CLI wiring.
|
|
95
|
+
4. Document the pure functions as a Jupyter notebook in
|
|
96
|
+
`docs/notebooks/<tool>.ipynb` (copy `docs/notebooks/example.ipynb` as a
|
|
97
|
+
starting point) and list it in `docs/docs/index.md`.
|
|
98
|
+
5. Run `make format`, `make check`, `make test`, and the pre-commit hooks
|
|
99
|
+
before opening a PR.
|
|
100
|
+
|
|
101
|
+
Full guidelines: [`AGENTS.md`](./AGENTS.md).
|
|
102
|
+
|
|
103
|
+
## Development
|
|
104
|
+
|
|
105
|
+
### Prerequisites
|
|
106
|
+
|
|
107
|
+
This project uses [Poetry](https://python-poetry.org/) for dependency management, inside a dedicated conda environment.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
make conda-env # create/verify the conda env and install dependencies
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Setup
|
|
114
|
+
|
|
115
|
+
1. **Install dependencies**:
|
|
116
|
+
```bash
|
|
117
|
+
make install
|
|
118
|
+
```
|
|
119
|
+
This installs the package and all development dependencies using Poetry.
|
|
120
|
+
|
|
121
|
+
2. **Install pre-commit hooks**:
|
|
122
|
+
```bash
|
|
123
|
+
make pre-commit
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Testing
|
|
127
|
+
|
|
128
|
+
Run the comprehensive test suite:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
make test
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Or run tests directly with Poetry:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
poetry run pytest -vvv
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Documentation
|
|
141
|
+
|
|
142
|
+
1. **Install docs dependencies**:
|
|
143
|
+
```bash
|
|
144
|
+
make docs
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
2. **Serve docs locally**:
|
|
148
|
+
```bash
|
|
149
|
+
make serve-docs
|
|
150
|
+
```
|
|
151
|
+
Or run directly with Poetry:
|
|
152
|
+
```bash
|
|
153
|
+
poetry run mkdocs serve -f docs/mkdocs.yml
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
3. **View documentation**: Open http://localhost:8000
|
|
157
|
+
|
|
158
|
+
### Code Quality
|
|
159
|
+
|
|
160
|
+
- **Format code**: `make format` or `poetry run black .`
|
|
161
|
+
- **Check formatting**: `make check` or `poetry run black --check --diff .`
|
|
162
|
+
- **Run linting**: `poetry run flake8`
|
|
163
|
+
- **Type checking**: `poetry run mypy .`
|
|
164
|
+
- **Clean artifacts**: `make clean`
|
|
165
|
+
|
|
166
|
+
### Docker Testing
|
|
167
|
+
|
|
168
|
+
Test the CLI in a clean container environment:
|
|
169
|
+
|
|
170
|
+
1. **Build image**:
|
|
171
|
+
```bash
|
|
172
|
+
make docker-image
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
2. **Run commands**:
|
|
176
|
+
```bash
|
|
177
|
+
docker run --rm gencli --help
|
|
178
|
+
docker run --rm gencli config list
|
|
179
|
+
docker run --rm gencli example slug "Hello, World!"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Configuration Storage
|
|
183
|
+
|
|
184
|
+
- **Default location**: `~/.gencli_config.json`
|
|
185
|
+
- **Custom location**: Set `GENCLI_CONFIG_PATH` environment variable
|
|
186
|
+
- **Format**: JSON with automatic type preservation
|
|
187
|
+
- **Default values**: Includes theme, output_format, auto_save, and debug settings
|
|
188
|
+
|
|
189
|
+
## Distribution
|
|
190
|
+
|
|
191
|
+
### PyPI Publishing (automated, recommended)
|
|
192
|
+
|
|
193
|
+
`.github/workflows/publish.yml` builds and publishes to PyPI whenever a
|
|
194
|
+
GitHub Release is published (or the workflow is run manually). It uses
|
|
195
|
+
[PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (OIDC)
|
|
196
|
+
— there is no API token to generate or store as a secret.
|
|
197
|
+
|
|
198
|
+
The PyPI distribution name is `gencli-agents` — the plain name `gencli`
|
|
199
|
+
is already taken by an unrelated project (`gen-cli`, which normalizes to
|
|
200
|
+
the same name once PyPI strips hyphens). The import name and CLI command
|
|
201
|
+
are unaffected and remain `gencli`.
|
|
202
|
+
|
|
203
|
+
**One-time setup** (do this before the first release):
|
|
204
|
+
|
|
205
|
+
1. On PyPI, go to "Add a new pending publisher"
|
|
206
|
+
(https://pypi.org/manage/account/publishing/, and the equivalent on
|
|
207
|
+
https://test.pypi.org/manage/account/publishing/ if you want to dry-run
|
|
208
|
+
via TestPyPI first) and add a trusted publisher with:
|
|
209
|
+
- PyPI Project Name: `gencli-agents`
|
|
210
|
+
- Owner: `gpadpoll`
|
|
211
|
+
- Repository: `general-cli`
|
|
212
|
+
- Workflow file: `publish.yml`
|
|
213
|
+
- Environment name: `pypi` (or `testpypi` on test.pypi.org)
|
|
214
|
+
2. The `pypi` and `testpypi` GitHub environments already exist on the repo
|
|
215
|
+
(Settings → Environments) — created for this workflow; add required
|
|
216
|
+
reviewers there if you want a manual approval gate before a publish
|
|
217
|
+
runs.
|
|
218
|
+
|
|
219
|
+
**To cut a release:**
|
|
220
|
+
|
|
221
|
+
1. Bump `version` in `pyproject.toml`.
|
|
222
|
+
2. Commit, then tag: `git tag vX.Y.Z && git push --tags`.
|
|
223
|
+
3. Create a GitHub Release from that tag. This triggers the workflow,
|
|
224
|
+
which verifies the tag matches the `pyproject.toml` version, builds the
|
|
225
|
+
sdist/wheel, and publishes them to PyPI.
|
|
226
|
+
|
|
227
|
+
### PyPI Publishing (manual)
|
|
228
|
+
|
|
229
|
+
> **NOTE**: Ensure you have a [PyPI account](https://pypi.org/account/register/) before publishing.
|
|
230
|
+
|
|
231
|
+
1. **Create distributions**:
|
|
232
|
+
```bash
|
|
233
|
+
make distributions
|
|
234
|
+
```
|
|
235
|
+
This builds the package using Poetry.
|
|
236
|
+
|
|
237
|
+
2. **Upload to PyPI**:
|
|
238
|
+
```bash
|
|
239
|
+
poetry publish
|
|
240
|
+
```
|
|
241
|
+
Or use twine:
|
|
242
|
+
```bash
|
|
243
|
+
twine upload dist/*
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Project layout
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
.
|
|
250
|
+
├── AGENTS.md # SDLC guidelines for agents adding/changing tools
|
|
251
|
+
├── Dockerfile # container image build steps
|
|
252
|
+
├── Makefile # convenience commands (install, test, docs, conda-env, etc.)
|
|
253
|
+
├── pyproject.toml # project metadata and dependencies (Poetry)
|
|
254
|
+
├── README.md # this file
|
|
255
|
+
├── .github/workflows/
|
|
256
|
+
│ ├── docs.yml # builds+deploys the MkDocs site to GitHub Pages
|
|
257
|
+
│ └── publish.yml # builds+publishes to PyPI on GitHub Release
|
|
258
|
+
├── scripts/
|
|
259
|
+
│ └── setup_conda_env.sh # creates/verifies the conda environment
|
|
260
|
+
├── docs/ # MkDocs site and notebook resources
|
|
261
|
+
│ ├── mkdocs.yml
|
|
262
|
+
│ ├── docs/
|
|
263
|
+
│ │ └── index.md
|
|
264
|
+
│ └── notebooks/
|
|
265
|
+
│ └── example.ipynb # documents the `example` tool's pure functions
|
|
266
|
+
├── gencli/ # main package code
|
|
267
|
+
│ ├── __init__.py
|
|
268
|
+
│ ├── constants.py
|
|
269
|
+
│ ├── main.py # top-level Typer app, registers command modules
|
|
270
|
+
│ ├── utils.py # shared pure helpers
|
|
271
|
+
│ └── commands/ # one module per tool (Typer sub-app)
|
|
272
|
+
│ ├── __init__.py
|
|
273
|
+
│ ├── config.py # configuration management commands
|
|
274
|
+
│ └── example.py # reference tool: pure functions + CLI wrapper
|
|
275
|
+
└── tests/
|
|
276
|
+
├── test_config.py
|
|
277
|
+
└── test_example.py
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Architecture
|
|
281
|
+
|
|
282
|
+
Built with modern Python CLI best practices:
|
|
283
|
+
|
|
284
|
+
- **[Poetry](https://python-poetry.org/)** - Modern dependency management
|
|
285
|
+
- **[Typer](https://typer.tiangolo.com/)** - Type-based CLI framework
|
|
286
|
+
- **[Rich](https://rich.readthedocs.io/)** - Beautiful terminal output
|
|
287
|
+
- **[Pytest](https://pytest.org/)** - Reliable testing framework
|
|
288
|
+
- **[MkDocs](https://mkdocs.org/)** - Professional documentation
|
|
289
|
+
- **[Black](https://black.readthedocs.io/)** - Code formatting
|
|
290
|
+
- **[Pre-commit](https://pre-commit.com/)** - Git hooks for quality
|
|
291
|
+
|
|
292
|
+
## Help
|
|
293
|
+
|
|
294
|
+
View all available make commands:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
make help
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Get CLI help:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
gencli --help
|
|
304
|
+
gencli config --help
|
|
305
|
+
gencli example --help
|
|
306
|
+
```
|
|
307
|
+
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# gencli
|
|
2
|
+
|
|
3
|
+
A general-purpose CLI: a library of functions exposed as commands, built for
|
|
4
|
+
agents to use and extend. Every tool is implemented as a small set of pure
|
|
5
|
+
functions with a thin Typer command wrapper, so the same logic works both as
|
|
6
|
+
a shell command and as a plain Python import (from a script, a notebook, or
|
|
7
|
+
another agent's tool).
|
|
8
|
+
|
|
9
|
+
See [`AGENTS.md`](./AGENTS.md) for the SDLC every new tool must follow
|
|
10
|
+
(pure functions, tests, and a documentation notebook per feature).
|
|
11
|
+
|
|
12
|
+
## Get Started
|
|
13
|
+
|
|
14
|
+
See the Docs page: https://gpadpoll.github.io/general-cli/
|
|
15
|
+
|
|
16
|
+
## Important: Poetry Version
|
|
17
|
+
|
|
18
|
+
To avoid compatibility errors (such as TypeError related to canonicalize_version), ensure you are using an up-to-date version of Poetry:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install --upgrade poetry
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If you encounter installation issues, upgrading Poetry usually resolves them.
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
1. **Environment**: create (or verify) the conda environment for this project
|
|
29
|
+
```bash
|
|
30
|
+
make conda-env
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
2. **Installation**: install the package in development mode
|
|
34
|
+
```bash
|
|
35
|
+
make install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
3. **Basic Usage**: try the built-in commands
|
|
39
|
+
```bash
|
|
40
|
+
gencli config list
|
|
41
|
+
gencli config set theme dark
|
|
42
|
+
gencli config get theme
|
|
43
|
+
|
|
44
|
+
gencli example slug "Hello, World!"
|
|
45
|
+
gencli example word-count "to be or not to be"
|
|
46
|
+
gencli example reverse "the quick brown fox"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## CLI Commands
|
|
50
|
+
|
|
51
|
+
- `gencli config` — manage CLI configuration (`set`, `get`, `list`, `reset`),
|
|
52
|
+
stored as JSON at `~/.gencli_config.json` (override with
|
|
53
|
+
`GENCLI_CONFIG_PATH`).
|
|
54
|
+
- `gencli example` — reference tool (`slug`, `word-count`, `reverse`). This
|
|
55
|
+
is the template to copy when adding a new tool: see
|
|
56
|
+
`gencli/commands/example.py`, its tests in `tests/test_example.py`, and
|
|
57
|
+
its notebook in `docs/notebooks/example.ipynb`.
|
|
58
|
+
|
|
59
|
+
## Adding a new tool
|
|
60
|
+
|
|
61
|
+
1. Create `gencli/commands/<tool>.py`. Implement the tool's logic as pure
|
|
62
|
+
functions (no I/O, no side effects, deterministic), and thin Typer
|
|
63
|
+
commands that call them.
|
|
64
|
+
2. Register the sub-app in `gencli/main.py`.
|
|
65
|
+
3. Write unit tests in `tests/test_<tool>.py`: direct tests for the pure
|
|
66
|
+
functions, plus a couple of `CliRunner` tests for the CLI wiring.
|
|
67
|
+
4. Document the pure functions as a Jupyter notebook in
|
|
68
|
+
`docs/notebooks/<tool>.ipynb` (copy `docs/notebooks/example.ipynb` as a
|
|
69
|
+
starting point) and list it in `docs/docs/index.md`.
|
|
70
|
+
5. Run `make format`, `make check`, `make test`, and the pre-commit hooks
|
|
71
|
+
before opening a PR.
|
|
72
|
+
|
|
73
|
+
Full guidelines: [`AGENTS.md`](./AGENTS.md).
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
### Prerequisites
|
|
78
|
+
|
|
79
|
+
This project uses [Poetry](https://python-poetry.org/) for dependency management, inside a dedicated conda environment.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
make conda-env # create/verify the conda env and install dependencies
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Setup
|
|
86
|
+
|
|
87
|
+
1. **Install dependencies**:
|
|
88
|
+
```bash
|
|
89
|
+
make install
|
|
90
|
+
```
|
|
91
|
+
This installs the package and all development dependencies using Poetry.
|
|
92
|
+
|
|
93
|
+
2. **Install pre-commit hooks**:
|
|
94
|
+
```bash
|
|
95
|
+
make pre-commit
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Testing
|
|
99
|
+
|
|
100
|
+
Run the comprehensive test suite:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
make test
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Or run tests directly with Poetry:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
poetry run pytest -vvv
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Documentation
|
|
113
|
+
|
|
114
|
+
1. **Install docs dependencies**:
|
|
115
|
+
```bash
|
|
116
|
+
make docs
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
2. **Serve docs locally**:
|
|
120
|
+
```bash
|
|
121
|
+
make serve-docs
|
|
122
|
+
```
|
|
123
|
+
Or run directly with Poetry:
|
|
124
|
+
```bash
|
|
125
|
+
poetry run mkdocs serve -f docs/mkdocs.yml
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
3. **View documentation**: Open http://localhost:8000
|
|
129
|
+
|
|
130
|
+
### Code Quality
|
|
131
|
+
|
|
132
|
+
- **Format code**: `make format` or `poetry run black .`
|
|
133
|
+
- **Check formatting**: `make check` or `poetry run black --check --diff .`
|
|
134
|
+
- **Run linting**: `poetry run flake8`
|
|
135
|
+
- **Type checking**: `poetry run mypy .`
|
|
136
|
+
- **Clean artifacts**: `make clean`
|
|
137
|
+
|
|
138
|
+
### Docker Testing
|
|
139
|
+
|
|
140
|
+
Test the CLI in a clean container environment:
|
|
141
|
+
|
|
142
|
+
1. **Build image**:
|
|
143
|
+
```bash
|
|
144
|
+
make docker-image
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
2. **Run commands**:
|
|
148
|
+
```bash
|
|
149
|
+
docker run --rm gencli --help
|
|
150
|
+
docker run --rm gencli config list
|
|
151
|
+
docker run --rm gencli example slug "Hello, World!"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Configuration Storage
|
|
155
|
+
|
|
156
|
+
- **Default location**: `~/.gencli_config.json`
|
|
157
|
+
- **Custom location**: Set `GENCLI_CONFIG_PATH` environment variable
|
|
158
|
+
- **Format**: JSON with automatic type preservation
|
|
159
|
+
- **Default values**: Includes theme, output_format, auto_save, and debug settings
|
|
160
|
+
|
|
161
|
+
## Distribution
|
|
162
|
+
|
|
163
|
+
### PyPI Publishing (automated, recommended)
|
|
164
|
+
|
|
165
|
+
`.github/workflows/publish.yml` builds and publishes to PyPI whenever a
|
|
166
|
+
GitHub Release is published (or the workflow is run manually). It uses
|
|
167
|
+
[PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (OIDC)
|
|
168
|
+
— there is no API token to generate or store as a secret.
|
|
169
|
+
|
|
170
|
+
The PyPI distribution name is `gencli-agents` — the plain name `gencli`
|
|
171
|
+
is already taken by an unrelated project (`gen-cli`, which normalizes to
|
|
172
|
+
the same name once PyPI strips hyphens). The import name and CLI command
|
|
173
|
+
are unaffected and remain `gencli`.
|
|
174
|
+
|
|
175
|
+
**One-time setup** (do this before the first release):
|
|
176
|
+
|
|
177
|
+
1. On PyPI, go to "Add a new pending publisher"
|
|
178
|
+
(https://pypi.org/manage/account/publishing/, and the equivalent on
|
|
179
|
+
https://test.pypi.org/manage/account/publishing/ if you want to dry-run
|
|
180
|
+
via TestPyPI first) and add a trusted publisher with:
|
|
181
|
+
- PyPI Project Name: `gencli-agents`
|
|
182
|
+
- Owner: `gpadpoll`
|
|
183
|
+
- Repository: `general-cli`
|
|
184
|
+
- Workflow file: `publish.yml`
|
|
185
|
+
- Environment name: `pypi` (or `testpypi` on test.pypi.org)
|
|
186
|
+
2. The `pypi` and `testpypi` GitHub environments already exist on the repo
|
|
187
|
+
(Settings → Environments) — created for this workflow; add required
|
|
188
|
+
reviewers there if you want a manual approval gate before a publish
|
|
189
|
+
runs.
|
|
190
|
+
|
|
191
|
+
**To cut a release:**
|
|
192
|
+
|
|
193
|
+
1. Bump `version` in `pyproject.toml`.
|
|
194
|
+
2. Commit, then tag: `git tag vX.Y.Z && git push --tags`.
|
|
195
|
+
3. Create a GitHub Release from that tag. This triggers the workflow,
|
|
196
|
+
which verifies the tag matches the `pyproject.toml` version, builds the
|
|
197
|
+
sdist/wheel, and publishes them to PyPI.
|
|
198
|
+
|
|
199
|
+
### PyPI Publishing (manual)
|
|
200
|
+
|
|
201
|
+
> **NOTE**: Ensure you have a [PyPI account](https://pypi.org/account/register/) before publishing.
|
|
202
|
+
|
|
203
|
+
1. **Create distributions**:
|
|
204
|
+
```bash
|
|
205
|
+
make distributions
|
|
206
|
+
```
|
|
207
|
+
This builds the package using Poetry.
|
|
208
|
+
|
|
209
|
+
2. **Upload to PyPI**:
|
|
210
|
+
```bash
|
|
211
|
+
poetry publish
|
|
212
|
+
```
|
|
213
|
+
Or use twine:
|
|
214
|
+
```bash
|
|
215
|
+
twine upload dist/*
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Project layout
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
.
|
|
222
|
+
├── AGENTS.md # SDLC guidelines for agents adding/changing tools
|
|
223
|
+
├── Dockerfile # container image build steps
|
|
224
|
+
├── Makefile # convenience commands (install, test, docs, conda-env, etc.)
|
|
225
|
+
├── pyproject.toml # project metadata and dependencies (Poetry)
|
|
226
|
+
├── README.md # this file
|
|
227
|
+
├── .github/workflows/
|
|
228
|
+
│ ├── docs.yml # builds+deploys the MkDocs site to GitHub Pages
|
|
229
|
+
│ └── publish.yml # builds+publishes to PyPI on GitHub Release
|
|
230
|
+
├── scripts/
|
|
231
|
+
│ └── setup_conda_env.sh # creates/verifies the conda environment
|
|
232
|
+
├── docs/ # MkDocs site and notebook resources
|
|
233
|
+
│ ├── mkdocs.yml
|
|
234
|
+
│ ├── docs/
|
|
235
|
+
│ │ └── index.md
|
|
236
|
+
│ └── notebooks/
|
|
237
|
+
│ └── example.ipynb # documents the `example` tool's pure functions
|
|
238
|
+
├── gencli/ # main package code
|
|
239
|
+
│ ├── __init__.py
|
|
240
|
+
│ ├── constants.py
|
|
241
|
+
│ ├── main.py # top-level Typer app, registers command modules
|
|
242
|
+
│ ├── utils.py # shared pure helpers
|
|
243
|
+
│ └── commands/ # one module per tool (Typer sub-app)
|
|
244
|
+
│ ├── __init__.py
|
|
245
|
+
│ ├── config.py # configuration management commands
|
|
246
|
+
│ └── example.py # reference tool: pure functions + CLI wrapper
|
|
247
|
+
└── tests/
|
|
248
|
+
├── test_config.py
|
|
249
|
+
└── test_example.py
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Architecture
|
|
253
|
+
|
|
254
|
+
Built with modern Python CLI best practices:
|
|
255
|
+
|
|
256
|
+
- **[Poetry](https://python-poetry.org/)** - Modern dependency management
|
|
257
|
+
- **[Typer](https://typer.tiangolo.com/)** - Type-based CLI framework
|
|
258
|
+
- **[Rich](https://rich.readthedocs.io/)** - Beautiful terminal output
|
|
259
|
+
- **[Pytest](https://pytest.org/)** - Reliable testing framework
|
|
260
|
+
- **[MkDocs](https://mkdocs.org/)** - Professional documentation
|
|
261
|
+
- **[Black](https://black.readthedocs.io/)** - Code formatting
|
|
262
|
+
- **[Pre-commit](https://pre-commit.com/)** - Git hooks for quality
|
|
263
|
+
|
|
264
|
+
## Help
|
|
265
|
+
|
|
266
|
+
View all available make commands:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
make help
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Get CLI help:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
gencli --help
|
|
276
|
+
gencli config --help
|
|
277
|
+
gencli example --help
|
|
278
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration management command for the CLI.
|
|
3
|
+
|
|
4
|
+
This module provides commands to manage configuration settings stored in a JSON file.
|
|
5
|
+
The configuration file location can be customized via the GENCLI_CONFIG_PATH environment variable.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
|
|
13
|
+
import typer
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.table import Table
|
|
16
|
+
|
|
17
|
+
app = typer.Typer(
|
|
18
|
+
no_args_is_help=True,
|
|
19
|
+
help="Manage configuration settings for the CLI application.",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
# Default configuration values
|
|
25
|
+
DEFAULT_CONFIG = {
|
|
26
|
+
"theme": "default",
|
|
27
|
+
"output_format": "table",
|
|
28
|
+
"auto_save": True,
|
|
29
|
+
"debug": False,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_config_path() -> Path:
|
|
34
|
+
"""
|
|
35
|
+
Get the configuration file path.
|
|
36
|
+
|
|
37
|
+
Returns the path from GENCLI_CONFIG_PATH environment variable if set,
|
|
38
|
+
otherwise returns the default path ~/.gencli_config.json.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Path: The configuration file path.
|
|
42
|
+
"""
|
|
43
|
+
config_path = os.getenv("GENCLI_CONFIG_PATH")
|
|
44
|
+
if config_path:
|
|
45
|
+
return Path(config_path)
|
|
46
|
+
return Path.home() / ".gencli_config.json"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_config() -> Dict[str, Any]:
|
|
50
|
+
"""
|
|
51
|
+
Load configuration from the JSON file.
|
|
52
|
+
|
|
53
|
+
If the file doesn't exist, returns the default configuration.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Dict[str, Any]: The configuration dictionary.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
typer.Exit: If the configuration file is corrupted.
|
|
60
|
+
"""
|
|
61
|
+
config_path = get_config_path()
|
|
62
|
+
|
|
63
|
+
if not config_path.exists():
|
|
64
|
+
return DEFAULT_CONFIG.copy()
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
with open(config_path, "r") as f:
|
|
68
|
+
config = json.load(f)
|
|
69
|
+
return config
|
|
70
|
+
except json.JSONDecodeError:
|
|
71
|
+
console.print(
|
|
72
|
+
f"[red]Error: Configuration file {config_path} is corrupted.[/red]"
|
|
73
|
+
)
|
|
74
|
+
raise typer.Exit(1)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
console.print(f"[red]Error reading configuration file: {e}[/red]")
|
|
77
|
+
raise typer.Exit(1)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def save_config(config: Dict[str, Any]) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Save configuration to the JSON file.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
config: The configuration dictionary to save.
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
typer.Exit: If the configuration file cannot be written.
|
|
89
|
+
"""
|
|
90
|
+
config_path = get_config_path()
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
# Ensure the directory exists
|
|
94
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
|
|
96
|
+
with open(config_path, "w") as f:
|
|
97
|
+
json.dump(config, f, indent=2)
|
|
98
|
+
|
|
99
|
+
console.print(f"[green]Configuration saved to {config_path}[/green]")
|
|
100
|
+
except Exception as e:
|
|
101
|
+
console.print(f"[red]Error saving configuration file: {e}[/red]")
|
|
102
|
+
raise typer.Exit(1)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@app.command()
|
|
106
|
+
def set(
|
|
107
|
+
key: str = typer.Argument(..., help="Configuration key to set"),
|
|
108
|
+
value: Optional[str] = typer.Argument(
|
|
109
|
+
None, help="Configuration value to set (will prompt if not provided)"
|
|
110
|
+
),
|
|
111
|
+
) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Set a configuration value.
|
|
114
|
+
|
|
115
|
+
If the value is not provided as an argument, you will be prompted to enter it interactively.
|
|
116
|
+
The value will be automatically converted to the appropriate type (bool, int, float, or string).
|
|
117
|
+
|
|
118
|
+
Examples:
|
|
119
|
+
gencli config set theme dark
|
|
120
|
+
gencli config set debug true
|
|
121
|
+
gencli config set timeout 30
|
|
122
|
+
"""
|
|
123
|
+
config = load_config()
|
|
124
|
+
|
|
125
|
+
if value is None:
|
|
126
|
+
value = typer.prompt(f"Enter value for '{key}'")
|
|
127
|
+
|
|
128
|
+
# Try to convert value to appropriate type
|
|
129
|
+
converted_value: Any = value
|
|
130
|
+
if value.lower() in ("true", "false"):
|
|
131
|
+
converted_value = value.lower() == "true"
|
|
132
|
+
elif value.isdigit():
|
|
133
|
+
converted_value = int(value)
|
|
134
|
+
elif value.replace(".", "").isdigit():
|
|
135
|
+
try:
|
|
136
|
+
converted_value = float(value)
|
|
137
|
+
except ValueError:
|
|
138
|
+
pass # Keep as string
|
|
139
|
+
|
|
140
|
+
config[key] = converted_value
|
|
141
|
+
save_config(config)
|
|
142
|
+
|
|
143
|
+
console.print(f"[green]Set {key} = {converted_value}[/green]")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@app.command()
|
|
147
|
+
def get(
|
|
148
|
+
key: str = typer.Argument(..., help="Configuration key to retrieve"),
|
|
149
|
+
) -> None:
|
|
150
|
+
"""
|
|
151
|
+
Retrieve a configuration value.
|
|
152
|
+
|
|
153
|
+
Shows the value of the specified configuration key. If the key doesn't exist,
|
|
154
|
+
an error message is displayed and the command exits with status 1.
|
|
155
|
+
|
|
156
|
+
Examples:
|
|
157
|
+
gencli config get theme
|
|
158
|
+
gencli config get debug
|
|
159
|
+
"""
|
|
160
|
+
config = load_config()
|
|
161
|
+
|
|
162
|
+
if key not in config:
|
|
163
|
+
console.print(
|
|
164
|
+
f"[red]Error: Configuration key '{key}' not found.[/red]"
|
|
165
|
+
)
|
|
166
|
+
raise typer.Exit(1)
|
|
167
|
+
|
|
168
|
+
value = config[key]
|
|
169
|
+
console.print(f"{key} = {value}")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@app.command()
|
|
173
|
+
def list() -> None:
|
|
174
|
+
"""
|
|
175
|
+
Display all configuration values in a formatted table.
|
|
176
|
+
|
|
177
|
+
Shows all current configuration settings in a rich-formatted table,
|
|
178
|
+
making it easy to see all settings at a glance.
|
|
179
|
+
|
|
180
|
+
The table includes the configuration key, current value, and the value type.
|
|
181
|
+
"""
|
|
182
|
+
config = load_config()
|
|
183
|
+
|
|
184
|
+
if not config:
|
|
185
|
+
console.print("[yellow]No configuration settings found.[/yellow]")
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
table = Table(title="Configuration Settings")
|
|
189
|
+
table.add_column("Key", style="cyan", no_wrap=True)
|
|
190
|
+
table.add_column("Value", style="green")
|
|
191
|
+
table.add_column("Type", style="blue")
|
|
192
|
+
|
|
193
|
+
for key, value in sorted(config.items()):
|
|
194
|
+
table.add_row(key, str(value), type(value).__name__)
|
|
195
|
+
|
|
196
|
+
console.print(table)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@app.command()
|
|
200
|
+
def reset() -> None:
|
|
201
|
+
"""
|
|
202
|
+
Reset configuration to default values.
|
|
203
|
+
|
|
204
|
+
This command will restore all configuration settings to their default values.
|
|
205
|
+
A confirmation prompt is shown before proceeding to prevent accidental resets.
|
|
206
|
+
|
|
207
|
+
Default values:
|
|
208
|
+
- theme: default
|
|
209
|
+
- output_format: table
|
|
210
|
+
- auto_save: true
|
|
211
|
+
- debug: false
|
|
212
|
+
"""
|
|
213
|
+
if not typer.confirm(
|
|
214
|
+
"Are you sure you want to reset all configuration to defaults?"
|
|
215
|
+
):
|
|
216
|
+
console.print("[yellow]Reset cancelled.[/yellow]")
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
save_config(DEFAULT_CONFIG.copy())
|
|
220
|
+
console.print("[green]Configuration reset to defaults.[/green]")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
if __name__ == "__main__":
|
|
224
|
+
app()
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example command module.
|
|
3
|
+
|
|
4
|
+
This module is the reference template for adding new tools to the CLI.
|
|
5
|
+
It demonstrates the pattern every command module should follow:
|
|
6
|
+
|
|
7
|
+
1. Pure functions hold all the logic. They take plain values in, return
|
|
8
|
+
plain values out, and have no side effects (no I/O, no printing, no
|
|
9
|
+
mutation of arguments or module state). These are the functions an
|
|
10
|
+
agent or another module would import and call directly.
|
|
11
|
+
2. Typer commands are thin adapters. They parse CLI arguments/options,
|
|
12
|
+
call the pure functions, and handle the only side effects allowed at
|
|
13
|
+
this layer: reading input and printing/writing output.
|
|
14
|
+
|
|
15
|
+
See AGENTS.md for the full SDLC guidelines (tests + notebook docs are
|
|
16
|
+
required for every new pure function exposed here).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import re
|
|
20
|
+
from collections import Counter
|
|
21
|
+
from typing import Dict
|
|
22
|
+
|
|
23
|
+
import typer
|
|
24
|
+
from rich.console import Console
|
|
25
|
+
|
|
26
|
+
app = typer.Typer(
|
|
27
|
+
no_args_is_help=True,
|
|
28
|
+
help="Example text utilities. Reference implementation for new tools.",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
console = Console()
|
|
32
|
+
|
|
33
|
+
_WORD_RE = re.compile(r"[A-Za-z0-9']+")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# --------------------------------------------------------------------------
|
|
37
|
+
# Pure functions (no I/O, no side effects, deterministic output for a given
|
|
38
|
+
# input). These are what should be imported and unit-tested directly, and
|
|
39
|
+
# what should be demonstrated in a docs notebook.
|
|
40
|
+
# --------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def slugify(text: str) -> str:
|
|
44
|
+
"""
|
|
45
|
+
Convert arbitrary text into a URL/filename-friendly slug.
|
|
46
|
+
|
|
47
|
+
Lowercases the text, strips characters that are not alphanumeric,
|
|
48
|
+
and joins words with hyphens.
|
|
49
|
+
"""
|
|
50
|
+
words = _WORD_RE.findall(text.lower())
|
|
51
|
+
return "-".join(words)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def word_count(text: str) -> Dict[str, int]:
|
|
55
|
+
"""
|
|
56
|
+
Count occurrences of each word in ``text``.
|
|
57
|
+
|
|
58
|
+
Words are matched case-insensitively and punctuation is ignored.
|
|
59
|
+
Returns a plain dict mapping word -> count.
|
|
60
|
+
"""
|
|
61
|
+
words = _WORD_RE.findall(text.lower())
|
|
62
|
+
return dict(Counter(words))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def reverse_words(text: str) -> str:
|
|
66
|
+
"""Reverse the order of whitespace-separated words in ``text``."""
|
|
67
|
+
return " ".join(reversed(text.split()))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# --------------------------------------------------------------------------
|
|
71
|
+
# Typer commands (thin I/O adapters over the pure functions above).
|
|
72
|
+
# --------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@app.command()
|
|
76
|
+
def slug(
|
|
77
|
+
text: str = typer.Argument(..., help="Text to convert into a slug"),
|
|
78
|
+
) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Print a URL/filename-friendly slug for TEXT.
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
gencli example slug "Hello, World!"
|
|
84
|
+
"""
|
|
85
|
+
console.print(slugify(text))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@app.command(name="word-count")
|
|
89
|
+
def word_count_command(
|
|
90
|
+
text: str = typer.Argument(..., help="Text to count words in"),
|
|
91
|
+
) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Print word occurrence counts for TEXT, most frequent first.
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
gencli example word-count "to be or not to be"
|
|
97
|
+
"""
|
|
98
|
+
counts = word_count(text)
|
|
99
|
+
for word, count in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])):
|
|
100
|
+
console.print(f"{word}: {count}")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@app.command()
|
|
104
|
+
def reverse(
|
|
105
|
+
text: str = typer.Argument(
|
|
106
|
+
..., help="Text whose word order should be reversed"
|
|
107
|
+
),
|
|
108
|
+
) -> None:
|
|
109
|
+
"""
|
|
110
|
+
Print TEXT with its word order reversed.
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
gencli example reverse "the quick brown fox"
|
|
114
|
+
"""
|
|
115
|
+
console.print(reverse_words(text))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
app()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entrypoint for CLI.
|
|
3
|
+
|
|
4
|
+
This is the main entry point for the gencli CLI application. It wires up
|
|
5
|
+
each command module (a Typer sub-app) under a top-level name. Add new
|
|
6
|
+
tools by creating a module in `gencli/commands/`, implementing the tool's
|
|
7
|
+
logic as pure functions with thin Typer command wrappers (see
|
|
8
|
+
`gencli/commands/example.py`), and registering it here.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
from gencli.commands import config, example
|
|
14
|
+
|
|
15
|
+
app = typer.Typer(
|
|
16
|
+
no_args_is_help=True,
|
|
17
|
+
help="General-purpose CLI: a library of functions exposed as commands.",
|
|
18
|
+
rich_markup_mode="rich",
|
|
19
|
+
)
|
|
20
|
+
app.add_typer(config.app, name="config", help="manage configuration settings")
|
|
21
|
+
app.add_typer(example.app, name="example", help="reference text utilities")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def gencli() -> None:
|
|
25
|
+
app()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
app()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for the CLI.
|
|
3
|
+
|
|
4
|
+
Keep this module for small, pure, reusable helpers shared across commands
|
|
5
|
+
(formatting, parsing, path handling, etc). Command modules should import
|
|
6
|
+
from here rather than duplicating logic. See AGENTS.md for the functional
|
|
7
|
+
programming conventions new helpers should follow.
|
|
8
|
+
"""
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gencli-agents"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "General-purpose CLI: a library of functions exposed as commands, built for agents to use and extend"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "gustavo.polleti", email = "gustavo.polleti@gmail.com"},
|
|
7
|
+
]
|
|
8
|
+
license = "MIT"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9,<4.0"
|
|
11
|
+
keywords = ["cli", "typer", "agents", "tooling"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3.9",
|
|
14
|
+
"Programming Language :: Python :: 3.10",
|
|
15
|
+
"Programming Language :: Python :: 3.11",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Environment :: Console",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"typer (>=0.17.0,<0.18.0)",
|
|
26
|
+
"rich (>=14.0.0,<15.0.0)",
|
|
27
|
+
"shellingham (>=1.5.0,<2.0.0)",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/gpadpoll/general-cli"
|
|
32
|
+
Repository = "https://github.com/gpadpoll/general-cli"
|
|
33
|
+
Documentation = "https://gpadpoll.github.io/general-cli/"
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
gencli = "gencli.main:gencli"
|
|
37
|
+
|
|
38
|
+
[tool.poetry]
|
|
39
|
+
packages = [{include = "gencli"}]
|
|
40
|
+
|
|
41
|
+
[tool.poetry.group.dev.dependencies]
|
|
42
|
+
pre-commit = "*"
|
|
43
|
+
tox = "*"
|
|
44
|
+
flake8 = "*"
|
|
45
|
+
black = "*"
|
|
46
|
+
mypy = "*"
|
|
47
|
+
|
|
48
|
+
[tool.poetry.group.docs.dependencies]
|
|
49
|
+
mkdocs-material = "^9.6.0"
|
|
50
|
+
mkdocs-markdownextradata-plugin = "^0.2.6"
|
|
51
|
+
# Needed to convert notebooks to markdown for inclusion in the MkDocs site
|
|
52
|
+
jupyter = "*"
|
|
53
|
+
nbconvert = "*"
|
|
54
|
+
|
|
55
|
+
[tool.poetry.group.test.dependencies]
|
|
56
|
+
pytest = "^8.4.0"
|
|
57
|
+
|
|
58
|
+
[tool.poetry.group.publish.dependencies]
|
|
59
|
+
twine = "^6.0.0"
|
|
60
|
+
|
|
61
|
+
[build-system]
|
|
62
|
+
requires = ["poetry-core>=2.0"]
|
|
63
|
+
build-backend = "poetry.core.masonry.api"
|
|
64
|
+
|
|
65
|
+
[tool.black]
|
|
66
|
+
line-length = 79
|
|
67
|
+
target-version = ['py39', 'py310', 'py311', 'py312', 'py313']
|
|
68
|
+
include = '\.pyi?$'
|
|
69
|
+
exclude = '''
|
|
70
|
+
/(
|
|
71
|
+
\.eggs
|
|
72
|
+
| \.git
|
|
73
|
+
| \.hg
|
|
74
|
+
| \.mypy_cache
|
|
75
|
+
| \.tox
|
|
76
|
+
| \venv
|
|
77
|
+
| \.venv
|
|
78
|
+
| _build
|
|
79
|
+
| buck-out
|
|
80
|
+
| build
|
|
81
|
+
| dist
|
|
82
|
+
# The following are specific to Black, you probably don't want those.
|
|
83
|
+
| blib2to3
|
|
84
|
+
| tests/data
|
|
85
|
+
| profiling
|
|
86
|
+
)/
|
|
87
|
+
'''
|