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.
Files changed (109) hide show
  1. csrd_utils-0.5.2/PKG-INFO +177 -0
  2. csrd_utils-0.5.2/README.md +162 -0
  3. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/pyproject.toml +1 -1
  4. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/__main__.py +57 -4
  5. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/__init__.py +2 -2
  6. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/operations.py +39 -0
  7. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/renderer.py +29 -5
  8. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/generate/handlers.py +150 -0
  9. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/generate/menu.py +15 -1
  10. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/__init__.py +2 -1
  11. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/menu.py +2 -2
  12. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/prompts.py +5 -5
  13. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/terminal.py +13 -0
  14. csrd_utils-0.5.0/PKG-INFO +0 -178
  15. csrd_utils-0.5.0/README.md +0 -163
  16. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/.gitignore +0 -0
  17. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/AGENTS.md +0 -0
  18. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/__init__.py +0 -0
  19. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/audit.py +0 -0
  20. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/augments.py +0 -0
  21. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/git.py +0 -0
  22. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/infra.py +0 -0
  23. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/loader.py +0 -0
  24. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/presets.py +0 -0
  25. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/scaffolder.py +0 -0
  26. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/service_renderers.py +0 -0
  27. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/compose/yaml_editor.py +0 -0
  28. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/doctor.py +0 -0
  29. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/generate/__init__.py +0 -0
  30. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/generate/helpers.py +0 -0
  31. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/models/__init__.py +0 -0
  32. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/models/base.py +0 -0
  33. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/models/spec.py +0 -0
  34. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/models/types.py +0 -0
  35. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/__init__.py +0 -0
  36. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/__init__.py +0 -0
  37. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/__init__.py +0 -0
  38. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/delegates/__init__.py +0 -0
  39. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/delegates/auth_delegate.py +0 -0
  40. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/dependencies/__init__.py +0 -0
  41. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/dependencies/auth_passthrough.py +0 -0
  42. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/models/__init__.py +0 -0
  43. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/models/auth_passthrough.py +0 -0
  44. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/views/__init__.py +0 -0
  45. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/auth-passthrough/views/auth_passthrough_view.py +0 -0
  46. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/caching/dependencies/cache.py.template +0 -0
  47. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/caching/views/cache_view.py.template +0 -0
  48. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/celery_client.py.template +0 -0
  49. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/models/an/__init__.py +0 -0
  50. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/models/an/tasks.py.template +0 -0
  51. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/models/tasks.py.template +0 -0
  52. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-dispatcher/views/tasks_view.py.template +0 -0
  53. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-worker/celery_app.py.template +0 -0
  54. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-worker/tasks/__init__.py +0 -0
  55. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/celery-worker/tasks/example.py.template +0 -0
  56. {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
  57. {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
  58. {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
  59. {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
  60. {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
  61. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/db-config/dependencies/db.py.template +0 -0
  62. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/gateway/dependencies/proxy.py.template +0 -0
  63. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/gateway/middleware/auth_guard.py.template +0 -0
  64. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/gateway/views/proxy_view.py.template +0 -0
  65. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-consumer/dependencies/auth.py.template +0 -0
  66. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/AUTH.md +0 -0
  67. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/__init__.py +0 -0
  68. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/__init__.py +0 -0
  69. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/auth.py +0 -0
  70. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/db.py.template +0 -0
  71. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/token_service.py +0 -0
  72. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/migrations.py.template +0 -0
  73. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/models/__init__.py +0 -0
  74. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/models/auth.py +0 -0
  75. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/models/jwks.py +0 -0
  76. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/models/users.py +0 -0
  77. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/repositories/__init__.py +0 -0
  78. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/repositories/user_repository.py +0 -0
  79. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/services/__init__.py +0 -0
  80. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/services/token_service.py +0 -0
  81. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/__init__.py +0 -0
  82. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/auth_view.py +0 -0
  83. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/jwks_view.py +0 -0
  84. {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
  85. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/jwt-auth-provider/views/users_view.py +0 -0
  86. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/metrics/middleware/metrics.py.template +0 -0
  87. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/metrics/views/metrics_view.py.template +0 -0
  88. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/rabbit-messaging/dependencies/rabbit.py.template +0 -0
  89. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/rabbit-messaging/handlers/example_handler.py.template +0 -0
  90. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/rabbit-messaging/handlers/ping_handler.py.template +0 -0
  91. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/rabbit-messaging/views/messaging_view.py.template +0 -0
  92. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/augments/tracing/middleware/tracing.py.template +0 -0
  93. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/cookiecutter.json +0 -0
  94. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  95. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  96. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  97. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  98. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
  99. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/models/health.py +0 -0
  100. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  101. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  102. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
  103. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
  104. {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
  105. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  106. {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
  107. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/exceptions.py +0 -0
  108. {csrd_utils-0.5.0 → csrd_utils-0.5.2}/src/tui_wizard/models.py +0 -0
  109. {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
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csrd-utils"
3
- version = "0.5.0"
3
+ version = "0.5.2"
4
4
  description = "CLI utilities for csrd service generation and feature augmentation"
5
5
  license = { text = "MIT" }
6
6
  requires-python = ">=3.12"
@@ -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" and getattr(args, "completion_shell", None) == "bash":
333
- print(_render_bash_completion(parser), end="")
334
- return 0
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
- "render_infra",
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 compose.\n\n"
313
- "## Next steps\n\n"
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 compose validate\n"
316
- "csrd compose plan\n"
317
- "```\n"
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