csrd-utils 0.5.0__tar.gz → 0.5.2__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.
- csrd_utils-0.5.2/PKG-INFO +177 -0
- csrd_utils-0.5.2/README.md +162 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/pyproject.toml +1 -1
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/__main__.py +57 -4
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/__init__.py +2 -2
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/operations.py +39 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/renderer.py +29 -5
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/generate/handlers.py +150 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/generate/menu.py +15 -1
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/__init__.py +2 -1
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/menu.py +2 -2
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/prompts.py +5 -5
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/terminal.py +13 -0
- csrd_utils-0.5.0/PKG-INFO +0 -178
- csrd_utils-0.5.0/README.md +0 -163
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/.gitignore +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/AGENTS.md +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/audit.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/augments.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/git.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/infra.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/loader.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/presets.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/scaffolder.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/service_renderers.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/yaml_editor.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/doctor.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/generate/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/generate/helpers.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/models/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/models/base.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/models/spec.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/models/types.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/delegates/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/delegates/auth_delegate.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/dependencies/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/dependencies/auth_passthrough.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/models/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/models/auth_passthrough.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/views/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/views/auth_passthrough_view.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/caching/dependencies/cache.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/caching/views/cache_view.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/celery_client.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/models/an/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/models/an/tasks.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/models/tasks.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/views/tasks_view.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-worker/celery_app.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-worker/tasks/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-worker/tasks/example.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/crud-scaffold/dependencies/${entity_name_snake}_repository.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/crud-scaffold/migrations_${entity_name_snake}.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/crud-scaffold/models/${entity_name_snake}.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/crud-scaffold/repositories/${entity_name_snake}_repository.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/crud-scaffold/views/${entity_name_snake}_view.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/db-config/dependencies/db.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/gateway/dependencies/proxy.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/gateway/middleware/auth_guard.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/gateway/views/proxy_view.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-consumer/dependencies/auth.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/AUTH.md +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/auth.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/db.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/token_service.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/migrations.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/models/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/models/auth.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/models/jwks.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/models/users.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/repositories/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/repositories/user_repository.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/services/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/services/token_service.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/auth_view.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/jwks_view.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/users_admin_view.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/users_view.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/metrics/middleware/metrics.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/metrics/views/metrics_view.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/rabbit-messaging/dependencies/rabbit.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/rabbit-messaging/handlers/example_handler.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/rabbit-messaging/handlers/ping_handler.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/rabbit-messaging/views/messaging_view.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/tracing/middleware/tracing.py.template +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/cookiecutter.json +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/models/health.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/tests/test_health.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/views/health_view.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/exceptions.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/models.py +0 -0
- {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/wizard.py +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: csrd-utils
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: CLI utilities for csrd service generation and feature augmentation
|
|
5
|
+
Project-URL: Repository, https://github.com/csrd-api/fastapi-common
|
|
6
|
+
Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/utils
|
|
7
|
+
Project-URL: Changelog, https://github.com/csrd-api/fastapi-common/blob/main/CHANGELOG.md
|
|
8
|
+
License: MIT
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Requires-Dist: cookiecutter<3,>=2.6
|
|
11
|
+
Requires-Dist: pydantic<3,>=2.6
|
|
12
|
+
Requires-Dist: pyyaml<7,>=6
|
|
13
|
+
Requires-Dist: ruamel-yaml<1,>=0.18
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# csrd-utils
|
|
17
|
+
|
|
18
|
+
CLI and runtime helpers for generating csrd services and augmenting existing services with optional features.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install "csrd-utils @ git+https://github.com/csrd-api/fastapi-common.git#subdirectory=packages/utils"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## CLI
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
csrd --help
|
|
30
|
+
csrd --version
|
|
31
|
+
|
|
32
|
+
# Workspace-level generation (compose-based)
|
|
33
|
+
csrd generate # interactive menu (context-aware)
|
|
34
|
+
csrd generate workspace --name my-ws # create empty workspace
|
|
35
|
+
csrd generate preset --name my-ws # create workspace from preset
|
|
36
|
+
csrd generate add-service --name inventory # add service to current workspace
|
|
37
|
+
csrd generate rename-service --service-name old-service --new-name new-service
|
|
38
|
+
csrd generate remove-service --service-name old # remove service from spec
|
|
39
|
+
csrd generate add-infra --infra-type postgres # add infra to workspace
|
|
40
|
+
csrd generate remove-infra --infra-type postgres # remove infra from workspace
|
|
41
|
+
csrd generate add-augment # interactive augment selection
|
|
42
|
+
csrd generate list-augments # list available augments
|
|
43
|
+
|
|
44
|
+
# Feature augmentation
|
|
45
|
+
csrd feature list
|
|
46
|
+
csrd feature plan workers --service .
|
|
47
|
+
csrd feature plan workers --service . --json
|
|
48
|
+
csrd feature add workers --service .
|
|
49
|
+
|
|
50
|
+
# Diagnostics
|
|
51
|
+
csrd doctor --service .
|
|
52
|
+
csrd audit # workspace-aware: scans all services
|
|
53
|
+
csrd audit --service . # explicitly audit one service path
|
|
54
|
+
|
|
55
|
+
# Shell completion
|
|
56
|
+
csrd completion bash # print bash completion script
|
|
57
|
+
csrd completion install # install to ~/.local/share/bash-completion/
|
|
58
|
+
csrd completion uninstall # remove installed completion
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`feature plan --json` is useful for CI or tooling wrappers.
|
|
62
|
+
|
|
63
|
+
### Bash tab completion
|
|
64
|
+
|
|
65
|
+
Install completion (one-time, persists across shells):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
csrd completion install
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This writes a completion script to `~/.local/share/bash-completion/completions/csrd`,
|
|
72
|
+
which bash auto-loads — no `.bashrc` edit needed. To remove: `csrd completion uninstall`.
|
|
73
|
+
|
|
74
|
+
Manual alternative:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
source <(csrd completion bash)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Non-TTY mode
|
|
81
|
+
|
|
82
|
+
Set `CSRD_NO_TTY=1` to force numbered-fallback prompts instead of the
|
|
83
|
+
arrow-key TUI. Useful for manual testing or piping input:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
CSRD_NO_TTY=1 csrd generate
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Interactive generation behavior
|
|
90
|
+
|
|
91
|
+
- `csrd generate` shows a context-aware menu (workspace actions when inside a workspace, workspace creation otherwise).
|
|
92
|
+
- Services, augments, and infra are managed via `csrd-compose.yaml` (the workspace spec).
|
|
93
|
+
- `csrd generate rename-service` renames the service in the spec, renames `src/`, `tests/`, and `Dockerfile.*`, and rewrites Python imports and string references.
|
|
94
|
+
- All yes/no prompts default to `No` (`y/N`).
|
|
95
|
+
|
|
96
|
+
## Typical workflow
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# 0) Create a workspace and add services
|
|
100
|
+
csrd generate workspace --name local-dev
|
|
101
|
+
cd local-dev
|
|
102
|
+
csrd generate add-service --name inventory --features database --port 8081
|
|
103
|
+
csrd generate add-service --name pricing --port 8082
|
|
104
|
+
|
|
105
|
+
# 1) Verify service compatibility
|
|
106
|
+
csrd doctor --service .
|
|
107
|
+
|
|
108
|
+
# 1b) Audit for weak/insecure defaults
|
|
109
|
+
csrd audit
|
|
110
|
+
|
|
111
|
+
# 2) Inspect available bundled features
|
|
112
|
+
csrd feature list
|
|
113
|
+
|
|
114
|
+
# 3) Dry-run a feature
|
|
115
|
+
csrd feature plan workers --service .
|
|
116
|
+
|
|
117
|
+
# 4) Apply feature files/merges
|
|
118
|
+
csrd feature add workers --service .
|
|
119
|
+
|
|
120
|
+
# 5) Rename a service
|
|
121
|
+
csrd generate rename-service --service-name pricing-service --new-name billing-service
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Bundled assets
|
|
125
|
+
|
|
126
|
+
- `features/workers/` feature manifests and fragments
|
|
127
|
+
- `templates/service/` template starter
|
|
128
|
+
|
|
129
|
+
## Python API
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from pathlib import Path
|
|
133
|
+
from csrd_utils import ServiceAugmentor
|
|
134
|
+
from csrd_utils.resources import features_path
|
|
135
|
+
|
|
136
|
+
with features_path() as feature_lib:
|
|
137
|
+
augmentor = ServiceAugmentor(Path("."), feature_lib)
|
|
138
|
+
ok, changes = augmentor.add_feature("workers", plan=True)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
- `docs/CLI_REFERENCE.md` - full command/flag reference
|
|
144
|
+
- `docs/FEATURES.md` - bundled feature catalog and behavior
|
|
145
|
+
- `docs/FUTURE_WORK.md` - remaining provider-specific CI wiring work (Phase 5B)
|
|
146
|
+
- `docs/AGENT_SMOKE_TESTS.md` - copy/paste validation flow for clean workspaces
|
|
147
|
+
- `docs/COMMAND_MATRIX.yaml` - machine-readable command matrix for automation
|
|
148
|
+
- `AGENTS.md` - package-local rules for autonomous agents
|
|
149
|
+
|
|
150
|
+
## CLI-only agent mode
|
|
151
|
+
|
|
152
|
+
If an agent can only see the installed CLI (not source files), use this exploration sequence:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
csrd --help
|
|
156
|
+
csrd generate --help
|
|
157
|
+
csrd feature --help
|
|
158
|
+
csrd doctor --help
|
|
159
|
+
csrd audit --help
|
|
160
|
+
csrd completion --help
|
|
161
|
+
csrd feature list
|
|
162
|
+
csrd generate list-augments
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Then run a clean smoke flow:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
mkdir -p /tmp/csrd-smoke && cd /tmp/csrd-smoke
|
|
169
|
+
csrd generate workspace --name ws
|
|
170
|
+
cd ws
|
|
171
|
+
csrd generate add-service --name demo-svc --port 8080
|
|
172
|
+
csrd doctor --service .
|
|
173
|
+
csrd audit
|
|
174
|
+
csrd feature plan workers --service .
|
|
175
|
+
csrd feature add workers --service .
|
|
176
|
+
csrd generate rename-service --service-name demo-svc-service --new-name renamed-svc
|
|
177
|
+
```
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# csrd-utils
|
|
2
|
+
|
|
3
|
+
CLI and runtime helpers for generating csrd services and augmenting existing services with optional features.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install "csrd-utils @ git+https://github.com/csrd-api/fastapi-common.git#subdirectory=packages/utils"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## CLI
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
csrd --help
|
|
15
|
+
csrd --version
|
|
16
|
+
|
|
17
|
+
# Workspace-level generation (compose-based)
|
|
18
|
+
csrd generate # interactive menu (context-aware)
|
|
19
|
+
csrd generate workspace --name my-ws # create empty workspace
|
|
20
|
+
csrd generate preset --name my-ws # create workspace from preset
|
|
21
|
+
csrd generate add-service --name inventory # add service to current workspace
|
|
22
|
+
csrd generate rename-service --service-name old-service --new-name new-service
|
|
23
|
+
csrd generate remove-service --service-name old # remove service from spec
|
|
24
|
+
csrd generate add-infra --infra-type postgres # add infra to workspace
|
|
25
|
+
csrd generate remove-infra --infra-type postgres # remove infra from workspace
|
|
26
|
+
csrd generate add-augment # interactive augment selection
|
|
27
|
+
csrd generate list-augments # list available augments
|
|
28
|
+
|
|
29
|
+
# Feature augmentation
|
|
30
|
+
csrd feature list
|
|
31
|
+
csrd feature plan workers --service .
|
|
32
|
+
csrd feature plan workers --service . --json
|
|
33
|
+
csrd feature add workers --service .
|
|
34
|
+
|
|
35
|
+
# Diagnostics
|
|
36
|
+
csrd doctor --service .
|
|
37
|
+
csrd audit # workspace-aware: scans all services
|
|
38
|
+
csrd audit --service . # explicitly audit one service path
|
|
39
|
+
|
|
40
|
+
# Shell completion
|
|
41
|
+
csrd completion bash # print bash completion script
|
|
42
|
+
csrd completion install # install to ~/.local/share/bash-completion/
|
|
43
|
+
csrd completion uninstall # remove installed completion
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`feature plan --json` is useful for CI or tooling wrappers.
|
|
47
|
+
|
|
48
|
+
### Bash tab completion
|
|
49
|
+
|
|
50
|
+
Install completion (one-time, persists across shells):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
csrd completion install
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This writes a completion script to `~/.local/share/bash-completion/completions/csrd`,
|
|
57
|
+
which bash auto-loads — no `.bashrc` edit needed. To remove: `csrd completion uninstall`.
|
|
58
|
+
|
|
59
|
+
Manual alternative:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
source <(csrd completion bash)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Non-TTY mode
|
|
66
|
+
|
|
67
|
+
Set `CSRD_NO_TTY=1` to force numbered-fallback prompts instead of the
|
|
68
|
+
arrow-key TUI. Useful for manual testing or piping input:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
CSRD_NO_TTY=1 csrd generate
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Interactive generation behavior
|
|
75
|
+
|
|
76
|
+
- `csrd generate` shows a context-aware menu (workspace actions when inside a workspace, workspace creation otherwise).
|
|
77
|
+
- Services, augments, and infra are managed via `csrd-compose.yaml` (the workspace spec).
|
|
78
|
+
- `csrd generate rename-service` renames the service in the spec, renames `src/`, `tests/`, and `Dockerfile.*`, and rewrites Python imports and string references.
|
|
79
|
+
- All yes/no prompts default to `No` (`y/N`).
|
|
80
|
+
|
|
81
|
+
## Typical workflow
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# 0) Create a workspace and add services
|
|
85
|
+
csrd generate workspace --name local-dev
|
|
86
|
+
cd local-dev
|
|
87
|
+
csrd generate add-service --name inventory --features database --port 8081
|
|
88
|
+
csrd generate add-service --name pricing --port 8082
|
|
89
|
+
|
|
90
|
+
# 1) Verify service compatibility
|
|
91
|
+
csrd doctor --service .
|
|
92
|
+
|
|
93
|
+
# 1b) Audit for weak/insecure defaults
|
|
94
|
+
csrd audit
|
|
95
|
+
|
|
96
|
+
# 2) Inspect available bundled features
|
|
97
|
+
csrd feature list
|
|
98
|
+
|
|
99
|
+
# 3) Dry-run a feature
|
|
100
|
+
csrd feature plan workers --service .
|
|
101
|
+
|
|
102
|
+
# 4) Apply feature files/merges
|
|
103
|
+
csrd feature add workers --service .
|
|
104
|
+
|
|
105
|
+
# 5) Rename a service
|
|
106
|
+
csrd generate rename-service --service-name pricing-service --new-name billing-service
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Bundled assets
|
|
110
|
+
|
|
111
|
+
- `features/workers/` feature manifests and fragments
|
|
112
|
+
- `templates/service/` template starter
|
|
113
|
+
|
|
114
|
+
## Python API
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from pathlib import Path
|
|
118
|
+
from csrd_utils import ServiceAugmentor
|
|
119
|
+
from csrd_utils.resources import features_path
|
|
120
|
+
|
|
121
|
+
with features_path() as feature_lib:
|
|
122
|
+
augmentor = ServiceAugmentor(Path("."), feature_lib)
|
|
123
|
+
ok, changes = augmentor.add_feature("workers", plan=True)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Documentation
|
|
127
|
+
|
|
128
|
+
- `docs/CLI_REFERENCE.md` - full command/flag reference
|
|
129
|
+
- `docs/FEATURES.md` - bundled feature catalog and behavior
|
|
130
|
+
- `docs/FUTURE_WORK.md` - remaining provider-specific CI wiring work (Phase 5B)
|
|
131
|
+
- `docs/AGENT_SMOKE_TESTS.md` - copy/paste validation flow for clean workspaces
|
|
132
|
+
- `docs/COMMAND_MATRIX.yaml` - machine-readable command matrix for automation
|
|
133
|
+
- `AGENTS.md` - package-local rules for autonomous agents
|
|
134
|
+
|
|
135
|
+
## CLI-only agent mode
|
|
136
|
+
|
|
137
|
+
If an agent can only see the installed CLI (not source files), use this exploration sequence:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
csrd --help
|
|
141
|
+
csrd generate --help
|
|
142
|
+
csrd feature --help
|
|
143
|
+
csrd doctor --help
|
|
144
|
+
csrd audit --help
|
|
145
|
+
csrd completion --help
|
|
146
|
+
csrd feature list
|
|
147
|
+
csrd generate list-augments
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Then run a clean smoke flow:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
mkdir -p /tmp/csrd-smoke && cd /tmp/csrd-smoke
|
|
154
|
+
csrd generate workspace --name ws
|
|
155
|
+
cd ws
|
|
156
|
+
csrd generate add-service --name demo-svc --port 8080
|
|
157
|
+
csrd doctor --service .
|
|
158
|
+
csrd audit
|
|
159
|
+
csrd feature plan workers --service .
|
|
160
|
+
csrd feature add workers --service .
|
|
161
|
+
csrd generate rename-service --service-name demo-svc-service --new-name renamed-svc
|
|
162
|
+
```
|
|
@@ -86,6 +86,43 @@ def _render_bash_completion(parser: argparse.ArgumentParser) -> str:
|
|
|
86
86
|
return "\n".join(lines) + "\n"
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
# Completion install/uninstall
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
_COMPLETION_DIR = Path.home() / ".local" / "share" / "bash-completion" / "completions"
|
|
94
|
+
_COMPLETION_FILE = _COMPLETION_DIR / "csrd"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _install_completion(parser: argparse.ArgumentParser) -> int:
|
|
98
|
+
"""Write the bash completion script to the user completions directory.
|
|
99
|
+
|
|
100
|
+
bash-completion auto-loads files from ``~/.local/share/bash-completion/completions/``
|
|
101
|
+
so no ``.bashrc`` edit is needed.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
_COMPLETION_DIR.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
already = _COMPLETION_FILE.is_file()
|
|
106
|
+
_COMPLETION_FILE.write_text(_render_bash_completion(parser), encoding="utf-8")
|
|
107
|
+
if already:
|
|
108
|
+
print(f"Updated bash completion at {_COMPLETION_FILE}")
|
|
109
|
+
else:
|
|
110
|
+
print(f"Installed bash completion to {_COMPLETION_FILE}")
|
|
111
|
+
print("Open a new shell to activate.")
|
|
112
|
+
return 0
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _uninstall_completion() -> int:
|
|
116
|
+
"""Remove the installed bash completion file."""
|
|
117
|
+
|
|
118
|
+
if _COMPLETION_FILE.is_file():
|
|
119
|
+
_COMPLETION_FILE.unlink()
|
|
120
|
+
print(f"Removed {_COMPLETION_FILE}")
|
|
121
|
+
else:
|
|
122
|
+
print("No completion file installed.")
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
|
|
89
126
|
# ---------------------------------------------------------------------------
|
|
90
127
|
# Parser
|
|
91
128
|
# ---------------------------------------------------------------------------
|
|
@@ -115,6 +152,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
115
152
|
"remove-infra",
|
|
116
153
|
"add-augment",
|
|
117
154
|
"remove-service",
|
|
155
|
+
"rename-service",
|
|
118
156
|
"list-augments",
|
|
119
157
|
"empty",
|
|
120
158
|
],
|
|
@@ -146,7 +184,13 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
146
184
|
"--service-name",
|
|
147
185
|
dest="service_name",
|
|
148
186
|
default=None,
|
|
149
|
-
help="Service name (for remove-service)",
|
|
187
|
+
help="Service name (for remove-service / rename-service)",
|
|
188
|
+
)
|
|
189
|
+
gen.add_argument(
|
|
190
|
+
"--new-name",
|
|
191
|
+
dest="new_name",
|
|
192
|
+
default=None,
|
|
193
|
+
help="New service name (for rename-service)",
|
|
150
194
|
)
|
|
151
195
|
|
|
152
196
|
# ── compose ───────────────────────────────────────────────────────
|
|
@@ -189,6 +233,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
189
233
|
completion = sub.add_parser("completion", help="Shell completion helpers")
|
|
190
234
|
completion_sub = completion.add_subparsers(dest="completion_shell", required=True)
|
|
191
235
|
completion_sub.add_parser("bash", help="Print bash completion script")
|
|
236
|
+
completion_sub.add_parser("install", help="Install bash completion to ~/.local/share")
|
|
237
|
+
completion_sub.add_parser("uninstall", help="Remove installed bash completion")
|
|
192
238
|
|
|
193
239
|
return parser
|
|
194
240
|
|
|
@@ -232,6 +278,7 @@ def main() -> int:
|
|
|
232
278
|
port=getattr(args, "port", None),
|
|
233
279
|
infra_type=getattr(args, "infra_type", None),
|
|
234
280
|
service_name=getattr(args, "service_name", None),
|
|
281
|
+
new_name=getattr(args, "new_name", None),
|
|
235
282
|
)
|
|
236
283
|
|
|
237
284
|
# ── compose ───────────────────────────────────────────────────────
|
|
@@ -329,9 +376,15 @@ def main() -> int:
|
|
|
329
376
|
print(f" fix: {finding.remediation}")
|
|
330
377
|
return 0 if audit_report.ok else 1
|
|
331
378
|
|
|
332
|
-
if args.command == "completion"
|
|
333
|
-
|
|
334
|
-
|
|
379
|
+
if args.command == "completion":
|
|
380
|
+
shell = getattr(args, "completion_shell", None)
|
|
381
|
+
if shell == "bash":
|
|
382
|
+
print(_render_bash_completion(parser), end="")
|
|
383
|
+
return 0
|
|
384
|
+
if shell == "install":
|
|
385
|
+
return _install_completion(parser)
|
|
386
|
+
if shell == "uninstall":
|
|
387
|
+
return _uninstall_completion()
|
|
335
388
|
|
|
336
389
|
parser.print_help()
|
|
337
390
|
return 0
|
|
@@ -32,7 +32,6 @@ from .infra import (
|
|
|
32
32
|
descriptor_for,
|
|
33
33
|
descriptors_for_feature,
|
|
34
34
|
detect_configured_db,
|
|
35
|
-
render_infra,
|
|
36
35
|
)
|
|
37
36
|
from .loader import (
|
|
38
37
|
SPEC_FILENAME,
|
|
@@ -55,6 +54,7 @@ from .operations import (
|
|
|
55
54
|
remove_infra,
|
|
56
55
|
remove_service,
|
|
57
56
|
remove_service_augment,
|
|
57
|
+
rename_service,
|
|
58
58
|
render_workspace,
|
|
59
59
|
validate,
|
|
60
60
|
)
|
|
@@ -123,7 +123,7 @@ __all__ = [
|
|
|
123
123
|
"remove_infra",
|
|
124
124
|
"remove_service",
|
|
125
125
|
"remove_service_augment",
|
|
126
|
-
"
|
|
126
|
+
"rename_service",
|
|
127
127
|
"render_workspace",
|
|
128
128
|
"save_spec",
|
|
129
129
|
"save_yaml",
|
|
@@ -451,6 +451,45 @@ def remove_service(output_dir: Path, service_name: str) -> ComposeSpec:
|
|
|
451
451
|
return spec
|
|
452
452
|
|
|
453
453
|
|
|
454
|
+
def rename_service(output_dir: Path, old_name: str, new_name: str) -> ComposeSpec:
|
|
455
|
+
"""Rename a service in the workspace spec.
|
|
456
|
+
|
|
457
|
+
Updates the service name and any cross-service augment ``targets``
|
|
458
|
+
options that reference the old name. Only updates the spec —
|
|
459
|
+
filesystem renames are the caller's responsibility.
|
|
460
|
+
|
|
461
|
+
Raises ``ValueError`` if *old_name* is not found or *new_name*
|
|
462
|
+
already exists.
|
|
463
|
+
"""
|
|
464
|
+
|
|
465
|
+
spec_path = spec_file_path(output_dir)
|
|
466
|
+
spec = load_spec(spec_path)
|
|
467
|
+
|
|
468
|
+
existing_names = {s.name for s in spec.services}
|
|
469
|
+
if old_name not in existing_names:
|
|
470
|
+
raise ValueError(f"Service '{old_name}' not found in workspace spec.")
|
|
471
|
+
if new_name in existing_names:
|
|
472
|
+
raise ValueError(f"Service '{new_name}' already exists in workspace spec.")
|
|
473
|
+
|
|
474
|
+
# Rename the service itself
|
|
475
|
+
svc = next(s for s in spec.services if s.name == old_name)
|
|
476
|
+
svc.name = new_name
|
|
477
|
+
|
|
478
|
+
# Update cross-service augment target references
|
|
479
|
+
for other_svc in spec.services:
|
|
480
|
+
for aug in other_svc.augments:
|
|
481
|
+
targets = aug.options.get("targets")
|
|
482
|
+
if targets is None:
|
|
483
|
+
continue
|
|
484
|
+
if isinstance(targets, str) and targets == old_name:
|
|
485
|
+
aug.options["targets"] = new_name
|
|
486
|
+
elif isinstance(targets, list):
|
|
487
|
+
aug.options["targets"] = [new_name if t == old_name else t for t in targets]
|
|
488
|
+
|
|
489
|
+
save_spec(spec, spec_path)
|
|
490
|
+
return spec
|
|
491
|
+
|
|
492
|
+
|
|
454
493
|
def next_available_port(spec: ComposeSpec, default: int = 8080) -> int:
|
|
455
494
|
"""Return the next unused port for a new service.
|
|
456
495
|
|
|
@@ -307,14 +307,29 @@ def _render_env_example(spec: ComposeSpec) -> str:
|
|
|
307
307
|
def _render_readme(spec: ComposeSpec) -> str:
|
|
308
308
|
"""Build a starter ``README.md`` for the workspace."""
|
|
309
309
|
|
|
310
|
+
svc_lines = ""
|
|
311
|
+
if spec.services:
|
|
312
|
+
svc_list = ", ".join(f"`{s.name}` (:{s.port})" for s in spec.services)
|
|
313
|
+
svc_lines = f"\n## Services\n\n{svc_list}\n"
|
|
314
|
+
|
|
310
315
|
return (
|
|
311
316
|
f"# {spec.workspace.name}\n\n"
|
|
312
|
-
"Generated by csrd
|
|
313
|
-
"
|
|
317
|
+
"Generated by [csrd](https://github.com/csrd-api/fastapi-common).\n"
|
|
318
|
+
f"{svc_lines}\n"
|
|
319
|
+
"## Quick start\n\n"
|
|
320
|
+
"```bash\n"
|
|
321
|
+
"docker compose up --build\n"
|
|
322
|
+
"```\n\n"
|
|
323
|
+
"## Workspace commands\n\n"
|
|
314
324
|
"```bash\n"
|
|
315
|
-
"csrd
|
|
316
|
-
"csrd
|
|
317
|
-
"
|
|
325
|
+
"csrd generate # interactive menu\n"
|
|
326
|
+
"csrd generate add-service # add a service\n"
|
|
327
|
+
"csrd generate rename-service # rename a service\n"
|
|
328
|
+
"csrd doctor # validate service layout\n"
|
|
329
|
+
"csrd audit # scan for insecure defaults\n"
|
|
330
|
+
"csrd compose validate # validate csrd-compose.yaml\n"
|
|
331
|
+
"```\n\n"
|
|
332
|
+
"Edit `csrd-compose.yaml` then run `csrd compose apply` to re-render.\n"
|
|
318
333
|
)
|
|
319
334
|
|
|
320
335
|
|
|
@@ -553,11 +568,20 @@ csrd compose apply
|
|
|
553
568
|
|
|
554
569
|
# Or use the interactive menu:
|
|
555
570
|
csrd generate
|
|
571
|
+
|
|
572
|
+
# Rename a service (updates spec, directories, code references):
|
|
573
|
+
csrd generate rename-service
|
|
574
|
+
|
|
575
|
+
# Install tab completion (one-time):
|
|
576
|
+
csrd completion install
|
|
556
577
|
```
|
|
557
578
|
|
|
558
579
|
Rendered files are regenerated from the spec. Scaffolded files (views,
|
|
559
580
|
repositories, services, dependencies) are never overwritten — your
|
|
560
581
|
custom code is safe.
|
|
582
|
+
|
|
583
|
+
Set `CSRD_NO_TTY=1` to force numbered-fallback prompts instead of the
|
|
584
|
+
arrow-key TUI (useful for scripting or CI).
|
|
561
585
|
"""
|
|
562
586
|
|
|
563
587
|
|