click-clop 0.3.0__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 (66) hide show
  1. click_clop-0.3.0/.gitignore +34 -0
  2. click_clop-0.3.0/.python-version +1 -0
  3. click_clop-0.3.0/CLAUDE.md +79 -0
  4. click_clop-0.3.0/Makefile +73 -0
  5. click_clop-0.3.0/PKG-INFO +278 -0
  6. click_clop-0.3.0/PLAN.md +179 -0
  7. click_clop-0.3.0/README.md +245 -0
  8. click_clop-0.3.0/pyproject.toml +70 -0
  9. click_clop-0.3.0/src/click_clop/__init__.py +16 -0
  10. click_clop-0.3.0/src/click_clop/__main__.py +5 -0
  11. click_clop-0.3.0/src/click_clop/cli.py +74 -0
  12. click_clop-0.3.0/src/click_clop/config.py +85 -0
  13. click_clop-0.3.0/src/click_clop/expose.py +181 -0
  14. click_clop-0.3.0/src/click_clop/logging.py +70 -0
  15. click_clop-0.3.0/src/click_clop/scaffold.py +68 -0
  16. click_clop-0.3.0/src/click_clop/secrets.py +172 -0
  17. click_clop-0.3.0/src/click_clop/service.py +126 -0
  18. click_clop-0.3.0/src/click_clop/system_detect.py +114 -0
  19. click_clop-0.3.0/src/click_clop/telemetry.py +61 -0
  20. click_clop-0.3.0/src/click_clop/template/.claude/settings.json.jinja +10 -0
  21. click_clop-0.3.0/src/click_clop/template/.git-hooks/commit-msg +25 -0
  22. click_clop-0.3.0/src/click_clop/template/.git-hooks/pre-commit +11 -0
  23. click_clop-0.3.0/src/click_clop/template/.git-templates/commit-message.txt +23 -0
  24. click_clop-0.3.0/src/click_clop/template/.gitignore.jinja +35 -0
  25. click_clop-0.3.0/src/click_clop/template/.python-version.jinja +1 -0
  26. click_clop-0.3.0/src/click_clop/template/CLAUDE.md.jinja +117 -0
  27. click_clop-0.3.0/src/click_clop/template/Dockerfile.jinja +44 -0
  28. click_clop-0.3.0/src/click_clop/template/Makefile.jinja +133 -0
  29. click_clop-0.3.0/src/click_clop/template/README.md.jinja +38 -0
  30. click_clop-0.3.0/src/click_clop/template/alloy/config.alloy.jinja +61 -0
  31. click_clop-0.3.0/src/click_clop/template/config.dev.toml.jinja +9 -0
  32. click_clop-0.3.0/src/click_clop/template/config.toml.jinja +18 -0
  33. click_clop-0.3.0/src/click_clop/template/copier.yml +42 -0
  34. click_clop-0.3.0/src/click_clop/template/docs/Makefile.jinja +16 -0
  35. click_clop-0.3.0/src/click_clop/template/docs/conf.py.jinja +23 -0
  36. click_clop-0.3.0/src/click_clop/template/docs/index.rst.jinja +50 -0
  37. click_clop-0.3.0/src/click_clop/template/features/hello.feature.jinja +24 -0
  38. click_clop-0.3.0/src/click_clop/template/features/steps/hello_steps.py.jinja +49 -0
  39. click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/Chart.yaml.jinja +11 -0
  40. click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/NOTES.txt.jinja +14 -0
  41. click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/_helpers.tpl.jinja +40 -0
  42. click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/configmap.yaml.jinja +16 -0
  43. click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/deployment.yaml.jinja +59 -0
  44. click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/onepassworditem.yaml.jinja +13 -0
  45. click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/service.yaml.jinja +20 -0
  46. click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/values.yaml.jinja +43 -0
  47. click_clop-0.3.0/src/click_clop/template/pyproject.toml.jinja +68 -0
  48. click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/__init__.py.jinja +3 -0
  49. click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/__main__.py.jinja +5 -0
  50. click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/cli.py.jinja +27 -0
  51. click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/config.py.jinja +10 -0
  52. click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/secrets.py.jinja +70 -0
  53. click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/server.py.jinja +56 -0
  54. click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/services/__init__.py.jinja +5 -0
  55. click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/services/hello.py.jinja +24 -0
  56. click_clop-0.3.0/src/click_clop/template/tests/conftest.py.jinja +23 -0
  57. click_clop-0.3.0/src/click_clop/template/tests/unit/test_hello.py.jinja +46 -0
  58. click_clop-0.3.0/tests/__init__.py +0 -0
  59. click_clop-0.3.0/tests/conftest.py +14 -0
  60. click_clop-0.3.0/tests/unit/__init__.py +0 -0
  61. click_clop-0.3.0/tests/unit/test_cli.py +27 -0
  62. click_clop-0.3.0/tests/unit/test_config.py +52 -0
  63. click_clop-0.3.0/tests/unit/test_expose.py +68 -0
  64. click_clop-0.3.0/tests/unit/test_service.py +92 -0
  65. click_clop-0.3.0/tests/unit/test_system_detect.py +31 -0
  66. click_clop-0.3.0/uv.lock +1894 -0
@@ -0,0 +1,34 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Testing
13
+ .pytest_cache/
14
+ .coverage
15
+ htmlcov/
16
+
17
+ # Type checking
18
+ .mypy_cache/
19
+
20
+ # IDE
21
+ .idea/
22
+ .vscode/
23
+ *.swp
24
+ *.swo
25
+
26
+ # OS
27
+ .DS_Store
28
+ Thumbs.db
29
+
30
+ # Local config
31
+ config.local.toml
32
+
33
+ # Claude Code worktrees
34
+ .claude/worktrees/
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,79 @@
1
+ # click-clop — Claude Code Guidelines
2
+
3
+ ## Project Overview
4
+
5
+ click-clop is a **framework + scaffold** for Python CLI applications.
6
+ It's both a pip-installable library and a project generator (`click-clop new <name>`).
7
+
8
+ Every feature in a generated project is simultaneously a CLI command (Click),
9
+ MCP tool (FastMCP), and REST endpoint (FastAPI) via the service-layer pattern.
10
+
11
+ ## Architecture
12
+
13
+ ```
14
+ ┌── click-clop (this project) ──────────────────┐
15
+ │ │
16
+ │ Library (pip install click-clop): │
17
+ │ service.py — Service base + registry │
18
+ │ expose.py — CLI/MCP/REST auto-wrappers │
19
+ │ config.py — TOML config loading │
20
+ │ secrets.py — 1Password CRUD │
21
+ │ telemetry.py — OpenTelemetry + Alloy │
22
+ │ logging.py — structlog setup │
23
+ │ system_detect.py — env detection │
24
+ │ │
25
+ │ Scaffold (click-clop new): │
26
+ │ cli.py — CLI entry point │
27
+ │ scaffold.py — Copier wrapper │
28
+ │ template/ — Copier template files │
29
+ └────────────────────────────────────────────────┘
30
+ ```
31
+
32
+ ## Directory Structure
33
+
34
+ ```
35
+ src/click_clop/
36
+ ├── __init__.py # Public API
37
+ ├── cli.py # click-clop CLI (new, detect)
38
+ ├── scaffold.py # Project generation
39
+ ├── service.py # Service pattern
40
+ ├── expose.py # Auto-exposure wrappers
41
+ ├── config.py # TOML config
42
+ ├── secrets.py # 1Password
43
+ ├── telemetry.py # OpenTelemetry
44
+ ├── logging.py # structlog
45
+ ├── system_detect.py # System probing
46
+ └── template/ # Copier template (generated projects)
47
+ ├── copier.yml
48
+ ├── src/{{module_name}}/
49
+ ├── tests/
50
+ ├── features/
51
+ ├── helm/
52
+ ├── docs/
53
+ ├── alloy/
54
+ ├── Makefile.jinja
55
+ ├── Dockerfile.jinja
56
+ ├── CLAUDE.md.jinja
57
+ └── ...
58
+ ```
59
+
60
+ ## Development
61
+
62
+ | Task | Command |
63
+ |------|---------|
64
+ | Install | `make install-dev` |
65
+ | Test | `make test` |
66
+ | Lint | `make lint` |
67
+ | Format | `make format` |
68
+ | Build | `make build` |
69
+
70
+ ## Agent Rules
71
+
72
+ When implementing changes to click-clop itself:
73
+
74
+ 1. **Use worktrees**: Use Claude Code's built-in `EnterWorktree` to work in isolation
75
+ 2. **Leave changes unstaged** for review
76
+ 3. **Service pattern**: core logic in service.py, thin wrappers in expose.py
77
+ 4. **Template changes**: update both the template files AND the self-hosting equivalents
78
+ 5. **Write tests**: `tests/unit/` for library code
79
+ 6. **Conventional commits**: `feat:`, `fix:`, `docs:`, etc.
@@ -0,0 +1,73 @@
1
+ # click-clop — Framework and scaffold for Python CLI applications
2
+ # This is click-clop's own Makefile (the framework project itself).
3
+
4
+ CONTAINER_RUNTIME ?= $(shell command -v podman 2>/dev/null || echo docker)
5
+ IMAGE_NAME ?= click-clop
6
+ IMAGE_TAG ?= latest
7
+ PYTEST ?= uv run pytest
8
+ BEHAVE ?= uv run behave
9
+
10
+ .DEFAULT_GOAL := help
11
+
12
+ .PHONY: help
13
+ help: ## Show this help message
14
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
15
+ awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
16
+
17
+ # ── Python / uv ──────────────────────────────────────────────
18
+
19
+ .PHONY: install
20
+ install: ## Install project dependencies
21
+ uv sync
22
+
23
+ .PHONY: install-dev
24
+ install-dev: ## Install with dev dependencies
25
+ uv sync --all-extras
26
+
27
+ .PHONY: lint
28
+ lint: ## Run linting (ruff + mypy)
29
+ uv run ruff check src/ tests/
30
+ uv run mypy src/
31
+
32
+ .PHONY: format
33
+ format: ## Format code with ruff
34
+ uv run ruff format src/ tests/
35
+ uv run ruff check --fix src/ tests/
36
+
37
+ .PHONY: test
38
+ test: ## Run all tests
39
+ $(PYTEST) tests/ -v
40
+
41
+ .PHONY: test-unit
42
+ test-unit: ## Run unit tests only
43
+ $(PYTEST) tests/unit -v
44
+
45
+ .PHONY: test-bdd
46
+ test-bdd: ## Run BDD/Gherkin feature tests
47
+ $(BEHAVE) features/
48
+
49
+ .PHONY: test-all
50
+ test-all: test test-bdd ## Run unit + BDD tests
51
+
52
+ .PHONY: coverage
53
+ coverage: ## Generate test coverage report
54
+ $(PYTEST) tests/ --cov=src/click_clop --cov-report=html --cov-report=term
55
+
56
+ # ── Container ────────────────────────────────────────────────
57
+
58
+ .PHONY: build-image
59
+ build-image: ## Build container image
60
+ $(CONTAINER_RUNTIME) build -t $(IMAGE_NAME):$(IMAGE_TAG) .
61
+
62
+ # ── Cleanup ──────────────────────────────────────────────────
63
+
64
+ .PHONY: clean
65
+ clean: ## Clean build artifacts
66
+ find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
67
+ find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null || true
68
+ find . -type d -name .mypy_cache -exec rm -rf {} + 2>/dev/null || true
69
+ rm -rf build/ dist/ *.egg-info .coverage htmlcov/
70
+
71
+ .PHONY: build
72
+ build: clean ## Build distribution package
73
+ uv build
@@ -0,0 +1,278 @@
1
+ Metadata-Version: 2.4
2
+ Name: click-clop
3
+ Version: 0.3.0
4
+ Summary: A reusable framework and scaffold for Python CLI applications with MCP, REST, and service-layer architecture
5
+ Author: bpayne
6
+ License: MIT
7
+ Keywords: cli,click,framework,mcp,scaffold
8
+ Requires-Python: >=3.14
9
+ Requires-Dist: click>=8.1.0
10
+ Requires-Dist: copier>=9.0.0
11
+ Requires-Dist: fastapi>=0.115.0
12
+ Requires-Dist: fastmcp>=2.0.0
13
+ Requires-Dist: opentelemetry-api>=1.20.0
14
+ Requires-Dist: opentelemetry-exporter-otlp>=1.20.0
15
+ Requires-Dist: opentelemetry-sdk>=1.20.0
16
+ Requires-Dist: questionary>=2.0.0
17
+ Requires-Dist: rich>=13.0.0
18
+ Requires-Dist: structlog>=24.0.0
19
+ Requires-Dist: tomli>=2.0.0; python_version < '3.11'
20
+ Requires-Dist: uvicorn>=0.34.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: behave>=1.2.6; extra == 'dev'
23
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
24
+ Requires-Dist: mypy>=1.0; extra == 'dev'
25
+ Requires-Dist: pytest-bdd>=7.0; extra == 'dev'
26
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.8.0; extra == 'dev'
29
+ Requires-Dist: sphinx-click>=6.0; extra == 'dev'
30
+ Requires-Dist: sphinx-rtd-theme>=2.0; extra == 'dev'
31
+ Requires-Dist: sphinx>=7.0; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # click-clop
35
+
36
+ A Python framework and project generator where every feature is simultaneously a **CLI command** (Click), **MCP tool** (FastMCP), and **REST endpoint** (FastAPI).
37
+
38
+ Write your business logic once as a service class — click-clop exposes it everywhere.
39
+
40
+ ```
41
+ Service method ──► CLI command (Click)
42
+ ──► MCP tool (FastMCP)
43
+ ──► REST endpoint (FastAPI)
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ### Install
49
+
50
+ ```bash
51
+ pip install click-clop
52
+ ```
53
+
54
+ ### Generate a new project
55
+
56
+ ```bash
57
+ click-clop new my-app --description "My awesome CLI"
58
+ cd my-app
59
+ make install-dev
60
+ ```
61
+
62
+ This scaffolds a complete project with a Makefile, Dockerfile, Helm charts, telemetry config, BDD tests, and documentation — with intelligent defaults based on your system.
63
+
64
+ ### Write a service
65
+
66
+ ```python
67
+ # src/my_app/services/greet.py
68
+ from click_clop import Service
69
+
70
+ class GreetService(Service):
71
+ """Greeting service."""
72
+
73
+ name = "greet"
74
+ description = "Say hello"
75
+
76
+ def hello(self, name: str = "world") -> str:
77
+ """Say hello to someone."""
78
+ return f"Hello, {name}!"
79
+
80
+ def farewell(self, name: str = "world") -> str:
81
+ """Say goodbye to someone."""
82
+ return f"Goodbye, {name}!"
83
+
84
+ _service = GreetService() # auto-registers
85
+ ```
86
+
87
+ ### Expose as CLI
88
+
89
+ ```python
90
+ import click
91
+ from click_clop import expose_cli
92
+
93
+ @click.group()
94
+ def main():
95
+ """My CLI app."""
96
+
97
+ expose_cli(main) # all registered services become subcommands
98
+ ```
99
+
100
+ ```bash
101
+ $ my-app greet hello --name Claude
102
+ Hello, Claude!
103
+ ```
104
+
105
+ ### Expose as REST
106
+
107
+ ```python
108
+ from click_clop import expose_rest
109
+
110
+ app = expose_rest() # creates /api/greet/hello, /api/greet/farewell
111
+ ```
112
+
113
+ ### Expose as MCP tools
114
+
115
+ ```python
116
+ from fastmcp import FastMCP
117
+ from click_clop import expose_mcp
118
+
119
+ mcp = FastMCP("my-service")
120
+ expose_mcp(mcp) # registers greet_hello, greet_farewell tools
121
+ ```
122
+
123
+ ## CLI
124
+
125
+ ```
126
+ click-clop new <PROJECT_NAME> Create a new project from template
127
+ -d, --description TEXT Project description
128
+ -a, --author TEXT Author name (auto-detected from git)
129
+ -o, --output-dir PATH Parent directory (default: cwd)
130
+
131
+ click-clop detect Show detected system capabilities
132
+ ```
133
+
134
+ ## Library API
135
+
136
+ ```python
137
+ from click_clop import (
138
+ Service, # Base class for services
139
+ ServiceRegistry, # Global registry of all services
140
+ load_config, # Load TOML config with env overrides
141
+ expose_cli, # Add services to a Click group
142
+ expose_mcp, # Add services as MCP tools
143
+ expose_rest, # Add services as FastAPI endpoints
144
+ )
145
+ ```
146
+
147
+ ### Configuration
148
+
149
+ `load_config()` merges TOML files in order (later wins):
150
+
151
+ 1. `config.toml` — base defaults
152
+ 2. `config.dev.toml` — development overrides
153
+ 3. `config.local.toml` — local/gitignored overrides
154
+ 4. Environment variables with prefix
155
+
156
+ ```python
157
+ from click_clop import load_config
158
+
159
+ config = load_config(prefix="APP")
160
+ ```
161
+
162
+ Environment variables map with double-underscore nesting:
163
+
164
+ ```
165
+ APP_SERVER__HOST=0.0.0.0 → config["server"]["host"]
166
+ APP_DATABASE__PORT=5432 → config["database"]["port"]
167
+ ```
168
+
169
+ ### Secrets (1Password)
170
+
171
+ ```python
172
+ from click_clop.secrets import get_secret_field, ensure_secret
173
+
174
+ # Read a secret
175
+ api_key = get_secret_field("vault-name", "item-name", "api_key")
176
+
177
+ # Create or update a secret
178
+ ensure_secret("vault-name", "item-name", {"api_key": "sk-..."})
179
+ ```
180
+
181
+ Requires the 1Password CLI (`op`) installed and authenticated.
182
+
183
+ ### Telemetry (OpenTelemetry)
184
+
185
+ ```python
186
+ from click_clop.telemetry import setup_telemetry, get_tracer
187
+
188
+ setup_telemetry(service_name="my-service", otlp_endpoint="http://localhost:4317")
189
+
190
+ tracer = get_tracer("my_module")
191
+ with tracer.start_as_current_span("operation"):
192
+ ...
193
+ ```
194
+
195
+ ### Logging (structlog)
196
+
197
+ ```python
198
+ from click_clop.logging import setup_logging, get_logger
199
+
200
+ setup_logging(level="INFO", json_output=True, service_name="my-service")
201
+ log = get_logger("my_module")
202
+ log.info("event", key="value")
203
+ ```
204
+
205
+ ### System Detection
206
+
207
+ ```python
208
+ from click_clop.system_detect import detect_system
209
+
210
+ info = detect_system()
211
+ print(info.summary())
212
+ ```
213
+
214
+ Detects Docker/Podman, Helm, 1Password CLI, Python, uv, Git, OS, and architecture.
215
+
216
+ ## Generated Project Structure
217
+
218
+ ```
219
+ my_app/
220
+ ├── src/my_app/
221
+ │ ├── __main__.py # Entry point
222
+ │ ├── cli.py # CLI main group
223
+ │ ├── server.py # MCP + REST server
224
+ │ └── services/
225
+ │ └── hello.py # Example service
226
+ ├── tests/
227
+ │ └── unit/
228
+ │ └── test_hello.py
229
+ ├── features/ # BDD/Gherkin tests
230
+ ├── helm/my_app/ # Kubernetes Helm charts
231
+ ├── alloy/config.alloy # Grafana Alloy telemetry config
232
+ ├── docs/ # Sphinx documentation
233
+ ├── config.toml # Application config
234
+ ├── Makefile # Development tasks
235
+ ├── Dockerfile
236
+ └── pyproject.toml
237
+ ```
238
+
239
+ ### Generated project commands
240
+
241
+ ```bash
242
+ make install-dev # Install with dev dependencies
243
+ make test # Run pytest
244
+ make test-bdd # Run BDD tests
245
+ make lint # Lint with ruff + mypy
246
+ make format # Format with ruff
247
+ make serve # Run REST + MCP server (Swagger at localhost:8000/docs)
248
+ make build-image # Build container image
249
+ make helm-install # Deploy via Helm
250
+ make docs # Build Sphinx docs
251
+ ```
252
+
253
+ ## Development
254
+
255
+ ```bash
256
+ git clone https://github.com/bpayne/click-clop.git
257
+ cd click-clop
258
+ make install-dev
259
+ make test
260
+ make lint
261
+ ```
262
+
263
+ ## Type Mapping
264
+
265
+ Service method parameters are automatically mapped:
266
+
267
+ | Python type | CLI (Click) | REST (FastAPI) |
268
+ |-------------|-------------|----------------|
269
+ | `str` | `STRING` | `str` |
270
+ | `int` | `INT` | `int` |
271
+ | `float` | `FLOAT` | `float` |
272
+ | `bool` | `--flag/--no-flag` | `bool` |
273
+
274
+ Parameters without type annotations default to `str`.
275
+
276
+ ## License
277
+
278
+ MIT
@@ -0,0 +1,179 @@
1
+ # click-clop Implementation Plan
2
+
3
+ ## Architecture
4
+
5
+ click-clop is **both** a library and a scaffold generator:
6
+
7
+ 1. **`click-clop` library** — pip-installable framework providing the service-layer pattern,
8
+ decorators, and utilities for building CLI+MCP+REST apps
9
+ 2. **`click-clop new <name>` CLI** — scaffolds a new project that depends on click-clop
10
+
11
+ ### Service Layer Pattern
12
+
13
+ ```
14
+ ┌─────────┐ ┌──────────┐ ┌──────────────┐
15
+ │ Click │ │ FastMCP │ │ FastAPI/REST │
16
+ │ CLI │ │ Server │ │ Endpoints │
17
+ └────┬────┘ └────┬─────┘ └──────┬───────┘
18
+ │ │ │
19
+ └────────────┼───────────────┘
20
+
21
+ ┌───────▼───────┐
22
+ │ Service Layer │ ← Core business logic lives here
23
+ │ (plain funcs) │
24
+ └───────┬───────┘
25
+
26
+ ┌───────▼───────┐
27
+ │ Config / │
28
+ │ Telemetry / │
29
+ │ Secrets │
30
+ └───────────────┘
31
+ ```
32
+
33
+ ## Directory Structure (click-clop itself)
34
+
35
+ ```
36
+ click-clop/
37
+ ├── src/
38
+ │ └── click_clop/
39
+ │ ├── __init__.py # Public API exports
40
+ │ ├── __main__.py # python -m click_clop
41
+ │ ├── cli.py # click-clop CLI (new, version, etc.)
42
+ │ ├── scaffold.py # Project generation logic
43
+ │ ├── service.py # ServiceBase class & registration
44
+ │ ├── expose.py # Thin wrapper generators (cli/mcp/rest)
45
+ │ ├── config.py # TOML config loading
46
+ │ ├── secrets.py # 1Password integration (read, create, update items)
47
+ │ ├── system_detect.py # Detect system capabilities (docker/podman, etc.)
48
+ │ ├── telemetry.py # OpenTelemetry + Alloy setup
49
+ │ ├── logging.py # Structured logging
50
+ │ └── template/ # Copier template (the scaffold)
51
+ │ ├── copier.yml # Copier config + wizard questions
52
+ │ ├── {{project_name}}/
53
+ │ │ ├── src/
54
+ │ │ │ └── {{module_name}}/
55
+ │ │ │ ├── __init__.py.jinja
56
+ │ │ │ ├── __main__.py.jinja
57
+ │ │ │ ├── cli.py.jinja
58
+ │ │ │ ├── server.py.jinja # MCP + REST server
59
+ │ │ │ ├── services/
60
+ │ │ │ │ ├── __init__.py.jinja
61
+ │ │ │ │ └── hello.py.jinja # Example service
62
+ │ │ │ ├── config.py.jinja
63
+ │ │ │ └── secrets.py.jinja # 1Password item CRUD helpers
64
+ │ │ ├── tests/
65
+ │ │ │ ├── conftest.py.jinja
66
+ │ │ │ └── unit/
67
+ │ │ │ └── test_hello.py.jinja
68
+ │ │ ├── features/
69
+ │ │ │ ├── steps/
70
+ │ │ │ │ └── hello_steps.py.jinja
71
+ │ │ │ └── hello.feature.jinja
72
+ │ │ ├── docs/
73
+ │ │ │ ├── conf.py.jinja
74
+ │ │ │ ├── index.rst.jinja
75
+ │ │ │ └── Makefile.jinja
76
+ │ │ ├── helm/
77
+ │ │ │ └── {{project_name}}/
78
+ │ │ │ ├── Chart.yaml.jinja
79
+ │ │ │ ├── values.yaml.jinja
80
+ │ │ │ └── templates/
81
+ │ │ │ ├── deployment.yaml.jinja
82
+ │ │ │ ├── service.yaml.jinja
83
+ │ │ │ ├── configmap.yaml.jinja
84
+ │ │ │ ├── onepassworditem.yaml.jinja # 1Password CRD
85
+ │ │ │ └── NOTES.txt.jinja
86
+ │ │ ├── .git-hooks/
87
+ │ │ │ ├── pre-commit
88
+ │ │ │ └── commit-msg
89
+ │ │ ├── .git-templates/
90
+ │ │ │ └── commit-message.txt
91
+ │ │ ├── alloy/
92
+ │ │ │ └── config.alloy.jinja
93
+ │ │ ├── CLAUDE.md.jinja
94
+ │ │ ├── .claude/
95
+ │ │ │ └── settings.json.jinja
96
+ │ │ ├── Makefile.jinja
97
+ │ │ ├── Dockerfile.jinja
98
+ │ │ ├── pyproject.toml.jinja
99
+ │ │ ├── config.toml.jinja
100
+ │ │ ├── config.dev.toml.jinja
101
+ │ │ ├── .gitignore.jinja
102
+ │ │ ├── .python-version.jinja
103
+ │ │ └── README.md.jinja
104
+ ├── tests/
105
+ │ ├── conftest.py
106
+ │ └── unit/
107
+ │ ├── test_service.py
108
+ │ ├── test_config.py
109
+ │ └── test_scaffold.py
110
+ ├── features/
111
+ │ ├── steps/
112
+ │ │ └── scaffold_steps.py
113
+ │ └── scaffold.feature
114
+ ├── docs/
115
+ │ └── conf.py
116
+ ├── CLAUDE.md
117
+ ├── Makefile
118
+ ├── Dockerfile
119
+ ├── pyproject.toml
120
+ ├── .gitignore
121
+ └── .python-version
122
+ ```
123
+
124
+ ## Implementation Steps
125
+
126
+ ### Phase 1: Core Library
127
+ 1. Set up src/ layout, pyproject.toml with all dependencies
128
+ 2. Implement `ServiceBase` — register plain functions, autodiscover services
129
+ 3. Implement `expose.py` — generate Click commands, FastMCP tools, FastAPI routes from services
130
+ 4. Implement `config.py` — TOML config loading with env overlay
131
+ 5. Implement `secrets.py` — 1Password integration (read, create, update, delete items via `op` CLI)
132
+ 6. Implement `telemetry.py` — OpenTelemetry SDK + OTLP export (Alloy-compatible)
133
+ 7. Implement `logging.py` — structlog with JSON output
134
+ 8. Implement `system_detect.py` — auto-detect system capabilities:
135
+ - Container runtime: `docker` vs `podman` (check which is available, prefer podman)
136
+ - Helm availability and version
137
+ - 1Password CLI (`op`) availability
138
+ - Python version, uv version
139
+ - Git version and config
140
+ - Available shell (bash/zsh)
141
+ - OS/platform info
142
+
143
+ ### Phase 2: Scaffold Generator
144
+ 9. Create copier template with minimal wizard (name, description, author)
145
+ - Wizard auto-detects and pre-fills: container runtime, python version, author from git config
146
+ - User confirms or overrides detected values
147
+ 10. Implement `click-clop new` CLI command (wraps copier + system detection)
148
+ 11. Template: Makefile with uv/docker/helm proxies
149
+ - Uses detected container runtime (`docker` or `podman`) as variable
150
+ 12. Template: Dockerfile (multi-stage, uv-based)
151
+ 13. Template: Helm chart
152
+ - Includes `OnePasswordItem` CRD resources for secret injection
153
+ - Chart.yaml declares 1password-connect dependency
154
+ - values.yaml has `onepassword.vault` and `onepassword.items[]` config
155
+ 14. Template: git hooks (pre-commit: lint+test, commit-msg: conventional commits)
156
+ 15. Template: git commit message template
157
+ 16. Template: CLAUDE.md + .claude/settings.json with agent rules
158
+ 17. Template: Sphinx docs with autodoc
159
+ 18. Template: Grafana Alloy config
160
+ - OTLP receiver for traces/metrics/logs from the app
161
+ - Prometheus remote-write export
162
+ - Loki log export
163
+ - Tempo trace export
164
+ - Configurable endpoints via config.toml
165
+ 19. Template: Gherkin feature file + behave steps
166
+ 20. Template: Swagger UI via FastAPI
167
+ 21. Template: config.toml + config.dev.toml
168
+ 22. Template: pyproject.toml with all tasks
169
+ 23. Template: .gitignore, .python-version, README.md
170
+ 24. Template: secrets.py — helper module for 1Password item CRUD in generated projects
171
+ - `create_secret(vault, title, fields)` — create a new 1Password item
172
+ - `get_secret(vault, title)` — read an item
173
+ - `update_secret(vault, title, fields)` — update fields
174
+ - `ensure_secret(vault, title, fields)` — idempotent create-or-update
175
+ - Exposed as CLI commands, MCP tools, and REST endpoints via the service layer
176
+
177
+ ### Phase 3: Self-hosting
178
+ 25. click-clop's own Makefile, Dockerfile, CLAUDE.md, tests
179
+ 26. Verify `click-clop new testapp` generates a working project