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.
- click_clop-0.3.0/.gitignore +34 -0
- click_clop-0.3.0/.python-version +1 -0
- click_clop-0.3.0/CLAUDE.md +79 -0
- click_clop-0.3.0/Makefile +73 -0
- click_clop-0.3.0/PKG-INFO +278 -0
- click_clop-0.3.0/PLAN.md +179 -0
- click_clop-0.3.0/README.md +245 -0
- click_clop-0.3.0/pyproject.toml +70 -0
- click_clop-0.3.0/src/click_clop/__init__.py +16 -0
- click_clop-0.3.0/src/click_clop/__main__.py +5 -0
- click_clop-0.3.0/src/click_clop/cli.py +74 -0
- click_clop-0.3.0/src/click_clop/config.py +85 -0
- click_clop-0.3.0/src/click_clop/expose.py +181 -0
- click_clop-0.3.0/src/click_clop/logging.py +70 -0
- click_clop-0.3.0/src/click_clop/scaffold.py +68 -0
- click_clop-0.3.0/src/click_clop/secrets.py +172 -0
- click_clop-0.3.0/src/click_clop/service.py +126 -0
- click_clop-0.3.0/src/click_clop/system_detect.py +114 -0
- click_clop-0.3.0/src/click_clop/telemetry.py +61 -0
- click_clop-0.3.0/src/click_clop/template/.claude/settings.json.jinja +10 -0
- click_clop-0.3.0/src/click_clop/template/.git-hooks/commit-msg +25 -0
- click_clop-0.3.0/src/click_clop/template/.git-hooks/pre-commit +11 -0
- click_clop-0.3.0/src/click_clop/template/.git-templates/commit-message.txt +23 -0
- click_clop-0.3.0/src/click_clop/template/.gitignore.jinja +35 -0
- click_clop-0.3.0/src/click_clop/template/.python-version.jinja +1 -0
- click_clop-0.3.0/src/click_clop/template/CLAUDE.md.jinja +117 -0
- click_clop-0.3.0/src/click_clop/template/Dockerfile.jinja +44 -0
- click_clop-0.3.0/src/click_clop/template/Makefile.jinja +133 -0
- click_clop-0.3.0/src/click_clop/template/README.md.jinja +38 -0
- click_clop-0.3.0/src/click_clop/template/alloy/config.alloy.jinja +61 -0
- click_clop-0.3.0/src/click_clop/template/config.dev.toml.jinja +9 -0
- click_clop-0.3.0/src/click_clop/template/config.toml.jinja +18 -0
- click_clop-0.3.0/src/click_clop/template/copier.yml +42 -0
- click_clop-0.3.0/src/click_clop/template/docs/Makefile.jinja +16 -0
- click_clop-0.3.0/src/click_clop/template/docs/conf.py.jinja +23 -0
- click_clop-0.3.0/src/click_clop/template/docs/index.rst.jinja +50 -0
- click_clop-0.3.0/src/click_clop/template/features/hello.feature.jinja +24 -0
- click_clop-0.3.0/src/click_clop/template/features/steps/hello_steps.py.jinja +49 -0
- click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/Chart.yaml.jinja +11 -0
- click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/NOTES.txt.jinja +14 -0
- click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/_helpers.tpl.jinja +40 -0
- click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/configmap.yaml.jinja +16 -0
- click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/deployment.yaml.jinja +59 -0
- click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/onepassworditem.yaml.jinja +13 -0
- click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/templates/service.yaml.jinja +20 -0
- click_clop-0.3.0/src/click_clop/template/helm/{{project_name}}/values.yaml.jinja +43 -0
- click_clop-0.3.0/src/click_clop/template/pyproject.toml.jinja +68 -0
- click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/__init__.py.jinja +3 -0
- click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/__main__.py.jinja +5 -0
- click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/cli.py.jinja +27 -0
- click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/config.py.jinja +10 -0
- click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/secrets.py.jinja +70 -0
- click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/server.py.jinja +56 -0
- click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/services/__init__.py.jinja +5 -0
- click_clop-0.3.0/src/click_clop/template/src/{{module_name}}/services/hello.py.jinja +24 -0
- click_clop-0.3.0/src/click_clop/template/tests/conftest.py.jinja +23 -0
- click_clop-0.3.0/src/click_clop/template/tests/unit/test_hello.py.jinja +46 -0
- click_clop-0.3.0/tests/__init__.py +0 -0
- click_clop-0.3.0/tests/conftest.py +14 -0
- click_clop-0.3.0/tests/unit/__init__.py +0 -0
- click_clop-0.3.0/tests/unit/test_cli.py +27 -0
- click_clop-0.3.0/tests/unit/test_config.py +52 -0
- click_clop-0.3.0/tests/unit/test_expose.py +68 -0
- click_clop-0.3.0/tests/unit/test_service.py +92 -0
- click_clop-0.3.0/tests/unit/test_system_detect.py +31 -0
- 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
|
click_clop-0.3.0/PLAN.md
ADDED
|
@@ -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
|