Release

All 11 DevForge CLI Tools Are Now Type-Safe

PEP 561 py.typed markers and full return-type annotations across every package. Mypy clean out of the box — no stub packages, no # type: ignore gymnastics.
May 28, 2026 · 7 min read · DevForge
Share this article:

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:

  1. py.typed marker — 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 to Any for every import, even if the source has annotations. With it, your mypy.ini doesn't need follow_imports = skip workarounds.
  2. 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 read def rotate(key_id: str, env: str) -> dict[str, Any]:. No more guessing whether you get a dict, a list, or None.
Before (untyped import): ┌──────────────────────────────────────────┐ │ import envault │ │ result = envault.rotate("prod_key", "staging") │ │ # mypy: result is Any ──→ no checking │ └──────────────────────────────────────────┘ After (PEP 561 typed import): ┌──────────────────────────────────────────┐ │ import envault │ │ result = envault.rotate("prod_key", "staging") │ │ # mypy: result is dict[str, Any] ──→ full checking │ └──────────────────────────────────────────┘

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:

ToolVersionpy.typedPublic functions annotated
envault0.1.022
apiauth0.2.018
apighost0.2.015
api-contract-guardian0.1.020
click-to-mcp0.5.016
configdrift0.1.014
deploydiff0.1.016
datamorph0.1.112
deadcode0.1.110
json2sql0.1.18
schemaforge1.7.024

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:

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:

  1. It requires annotating every public function (not just the CLI entry point)
  2. It forces you to fix every mypy error you expose — and those compound
  3. It adds package-data config to pyproject.toml to include the marker in wheels
  4. 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."

The result: 11 packages, 175+ annotated public functions, 0 mypy errors. Install any DevForge tool and your type checker works immediately — no stubs, no config, no workarounds.

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.

Share this article

Get Early Access

PyPI publishing is coming soon. Leave your email and we'll notify you the moment these tools ship.

✓ You're on the list. We'll email you when tools launch.

Star us on GitHub · View Pricing