Metadata-Version: 2.4
Name: curfew
Version: 0.1.0
Summary: Local-first Python dependency & module-boundary checker. No network, no binaries, zero runtime deps.
Project-URL: Homepage, https://github.com/OpenAfterHours/curfew
Project-URL: Repository, https://github.com/OpenAfterHours/curfew
Project-URL: Documentation, https://openafterhours.github.io/curfew/
Project-URL: Issues, https://github.com/OpenAfterHours/curfew/issues
Author: OpenAfterHours
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: architecture,boundaries,dependencies,imports,linter,module-boundaries,monorepo,tach,uv,workspace
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: rich
Requires-Dist: rich>=13; extra == 'rich'
Description-Content-Type: text/markdown

# curfew

> *couvre-feu* — "cover the fire". An after-hours rule that keeps things within
> their bounds. That's exactly what this tool does for your imports.

**curfew** is a local-first Python dependency & module-boundary checker — an
opinionated, fully-offline alternative to [`tach`](https://github.com/gauge-sh/tach).
From a single static import-graph engine it does two jobs:

1. **Module boundaries** — enforce which first-party modules/packages may import
   which (architecture / dependency-direction enforcement).
2. **External & workspace validation** — for each package, verify every imported
   third-party package is declared in that package's `pyproject.toml`, flag
   declared-but-unused dependencies, and detect **uv workspace leakage** (a
   member importing a package that only resolves because a *sibling* member
   declared it in the shared environment).

It also **visualises the dependency graph entirely offline**.

## Why curfew over tach?

`tach` is excellent, but its graph either **uploads your module structure to a
remote web viewer** (`tach show --web`) or emits GraphViz DOT that needs the
**GraphViz binary installed** to render. In a locked-down bank/regulatory
environment, neither is acceptable.

`curfew` is **local by construction**:

- **No network, ever.** No telemetry, no remote rendering, no "phone home".
- **Zero runtime dependencies.** `uv tool install curfew` pulls in nothing —
  the engine and CLI run on the standard library alone. (Colour is an optional
  `[rich]` extra.)
- **No mandatory binaries.** The default graph output is **Mermaid**, which
  renders in GitHub and MkDocs/Zensical with nothing installed. DOT is offered
  for those who already have GraphViz, but is never required.
- **One tool, both checks.** Module boundaries *and* workspace/dependency
  validation share one import graph.

## Install

```sh
uv tool install curfew          # zero transitive runtime dependencies
uv tool install "curfew[rich]"  # optional colour output
```

## Usage

```sh
curfew check                    # run all configured checks; non-zero exit on any error (CI gate)
curfew check --boundaries       # module-boundary check only
curfew check --externals        # external/workspace check only
curfew show --mermaid -o graph.md   # dependency graph as Mermaid (default)
curfew show --dot -o graph.dot      # dependency graph as GraphViz DOT (optional)
curfew report path/to/module.py     # dependencies and dependents of a module
curfew init                     # scaffold a [tool.curfew] config from your current structure
```

> **Run curfew inside your workspace venv.** Workspace-leakage detection relies on
> the single shared environment that `uv` creates for a workspace.

## Configuration

Config lives in a `[tool.curfew]` table in `pyproject.toml` (uv-native), with an
optional standalone `curfew.toml` override.

```toml
[tool.curfew]
source_roots = ["src"]

[tool.curfew.modules."rwa_calc.core"]
depends_on = []                    # leaf — may import nothing first-party
interface = ["api"]                # only rwa_calc.core.api is public
interface_enforced = true

[tool.curfew.modules."rwa_calc.io"]
depends_on    = ["rwa_calc.core", "rwa_calc.types"]
deprecated_on = ["rwa_calc.legacy"]   # allowed but reported as a warning (burndown)

[tool.curfew.external]
ignore = ["setuptools"]            # never flag these as undeclared
```

A module that **has** a rule is default-deny (only its `depends_on` plus its own
subtree are allowed). A module with **no** rule is unrestricted — so you can adopt
curfew incrementally. Set `default_deny = true` to require a rule on every
first-party module.

## Status

Alpha. Built under the [OpenAfterHours](https://github.com/OpenAfterHours) org.

## License

Apache-2.0. See [LICENSE](LICENSE).
