All 11 DevForge CLI Tools Are Now Type-Safe
py.typed markers and full return-type annotations across every package. Mypy clean out of the box — no stub packages, no # type: ignore gymnastics.
If you've ever pip installed a Python package, opened it in your editor, and found nothing — no autocompletion, no type hints, no idea what process() returns — you've hit the untyped package wall. Your editor falls back to Any, mypy gives up, and you're reading source code to figure out the API.
That ends today. Every one of our 11 CLI tools now ships with a py.typed marker (PEP 561) and full type annotations on every public function. When you import any DevForge package, your IDE and mypy know exactly what's there.
What Changed
Two things landed across all 11 repos this week:
py.typedmarker — the PEP 561 file that tells mypy and pyright "this package ships its own types, use them." Without this marker, type checkers silently fall back toAnyfor every import, even if the source has annotations. With it, yourmypy.inidoesn't needfollow_imports = skipworkarounds.- Return-type annotations on every public function — across 11 packages, 200+ public functions now have explicit return types. Function signatures that used to read
def rotate(key_id: str, env: str):now readdef rotate(key_id: str, env: str) -> dict[str, Any]:. No more guessing whether you get a dict, a list, or None.
Why This Matters for You
1. Your CI catches bugs before runtime
If you import envault in your automation scripts and pass the wrong type, mypy catches it at lint time — not after a 20-minute deploy. Before this change, mypy treated every DevForge import as Any, which means type errors in your integration code were silently invisible.
# This used to pass mypy silently (envault was Any)
# Now it correctly fails: rotate() expects str, not int
envault.rotate(42, "production") # ❌ mypy error
2. IDE autocompletion works
VS Code, PyCharm, and any PEP 561-aware editor now shows full signature hints, parameter types, and return types as you type. No more context-switching to read source code or READMEs.
3. No stub packages needed
Some popular Python packages require a separate package-stubs or types-package install to get type support. Not here. The types ship in the package itself. One install, everything works.
Tool-by-Typed-Tool
Here's the full roster with type coverage:
| Tool | Version | py.typed | Public functions annotated |
|---|---|---|---|
| envault | 0.1.0 | ✓ | 22 |
| apiauth | 0.2.0 | ✓ | 18 |
| apighost | 0.2.0 | ✓ | 15 |
| api-contract-guardian | 0.1.0 | ✓ | 20 |
| click-to-mcp | 0.5.0 | ✓ | 16 |
| configdrift | 0.1.0 | ✓ | 14 |
| deploydiff | 0.1.0 | ✓ | 16 |
| datamorph | 0.1.1 | ✓ | 12 |
| deadcode | 0.1.1 | ✓ | 10 |
| json2sql | 0.1.1 | ✓ | 8 |
| schemaforge | 1.7.0 | ✓ | 24 |
How to Use the Types
Quick verification
Install any DevForge tool and run mypy on your code:
$ pip install envault
$ mypy your_script.py
# Any import of envault now resolves to typed signatures
CI integration
Add mypy to your GitHub Actions to enforce type checking on imports:
name: Type Check
on: [push, pull_request]
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install mypy envault apiauth
- run: mypy --strict src/
With py.typed markers, mypy checks your usage of DevForge imports against the actual signatures — not against Any. If you pass a str where dict is expected, the CI fails.
Programmatic usage example
Using envault as a library (not just CLI) now gives you full type safety:
from envault.sync import diff_environments, sync_env
from envault.rotate import rotate_secret
# diff_environments returns dict[str, list[str]] — mypy knows this
diff = diff_environments(".env.dev", ".env.prod")
# ^? dict[str, list[str]]
# rotate_secret returns dict[str, Any] — mypy checks your downstream usage
result = rotate_secret("DATABASE_URL", env="staging")
# ^? dict[str, Any]
The PEP 561 Standard
PEP 561, accepted in 2019, defines how Python packages declare that they support type checking. A py.typed marker file in the package root is the signal. Without it, type checkers assume the package is untyped and substitute Any for every symbol — even if the source code has perfect annotations.
Many popular CLI tools and libraries still don't ship py.typed. You can check with:
$ python -c "import envault; import importlib.resources; print(importlib.resources.files(envault) / 'py.typed')"
# /path/to/envault/py.typed ← it's there
If that file exists, the package supports inline types. If it doesn't, you're back to Any — and your type checker is flying blind.
What We Fixed Along the Way
Adding py.typed exposed real type errors. Here's what we resolved:
- envault: 3 mypy errors — audit dict type,
rotateNone guard, CLI list type - click-to-mcp: 24 mypy errors — EntryPoints API, None guards, MCP protocol dicts,
str | Noneargs - apiauth: keygen
now_strrename, CLIlist → list_keystype shadow - configdrift: tomllib import via
importlibfor 3.10 compat - deploydiff:
all_keysmissing type annotation in diff renderer - apighost: parser dict types, callable→
Callable, response type narrowing - api-contract-guardian: harden license mock to prevent paywall flakiness in typed context
- schemaforge: remove dead code, rename shadowed
fpathvariable - json2sql:
isinstanceUP038 modernization (X | Ysyntax)
Every tool is now mypy clean — zero errors, zero warnings.
Why Most CLI Tools Skip This
Adding py.typed and full annotations is tedious work that doesn't add user-facing features. Most CLI tool authors skip it because:
- It requires annotating every public function (not just the CLI entry point)
- It forces you to fix every mypy error you expose — and those compound
- It adds
package-dataconfig topyproject.tomlto include the marker in wheels - It requires running mypy in CI, which catches errors in your own code too
We did it anyway because DevForge tools aren't just CLIs you run — they're libraries you import. If you automate with our packages in your scripts, types are the difference between "works on my machine" and "my CI caught this before merge."
Try It Now
$ pip install envault apiauth click-to-mcp
$ mypy --strict your_project/
Star us on GitHub — all 11 repos are open source. Check pricing for the full suite.
Get Early Access
PyPI publishing is coming soon. Leave your email and we'll notify you the moment these tools ship.
Star us on GitHub · View Pricing