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.
@@ -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
+ ```
@@ -0,0 +1,12 @@
1
+ """
2
+ Object initialization for CLI
3
+ """
4
+
5
+ from rich.console import Console
6
+
7
+ console = Console()
8
+
9
+
10
+ def hello() -> None:
11
+ """Print a friendly greeting confirming the package is importable."""
12
+ console.print("Hello from gencli!")
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,7 @@
1
+ """
2
+ Constants for the CLI.
3
+ """
4
+
5
+ WELCOME_MESSAGE = (
6
+ "Welcome to [bold green]gencli!![/bold green] Your CLI is working!"
7
+ )
@@ -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
+ '''