On this page
Start with uv, pip, Poetry, PDM and Pipenv
What the README and the knowledge base say about these tools, on one page.
Five-minute start
npx depcurfew doctorworks beforeinit: withoutdepcurfew.jsonit compares the repository with the policyinitwould write and shows, as notes in a block of their own, whatinitandapplywould change, for example a package manager that installs new versions without a release-age delay, with the settingsapplywould write for it. These notes never make it fail.npx depcurfew initwritesdepcurfew.jsonwith the default policy below and lists the tools it detected. When the repository already sets a longer release-age delay for one of them,cooldown.minimumkeeps the longest one, andinitsays where it found it.npx depcurfew explainshows what it detected and what the policy means for each tool.npx depcurfew apply --dry-runprints the changes as a diff, without writing anything.npx depcurfew applywrites them. Run it again wheneverdepcurfew.jsonchanges; a second run with the same policy changes nothing.npx depcurfew doctorreports configuration that silently does not work and drift from the policy, and exits with 1 when it finds an error or, with the"doctor": { "failOn": "warning" }thatinitwrites, a warning.- Add the GitHub Action so that doctor runs on every push and reports to code scanning.
The default policy:
{
"$schema": "https://unpkg.com/depcurfew/schema.json",
"version": 1,
"cooldown": {
"minimum": "3d",
"exclude": [],
"securityUpdates": "bypass"
},
"installScripts": {
"default": "deny",
"allow": [],
"deny": []
},
"sources": {
"git": "deny",
"remoteTarball": "deny",
"file": "allow",
"directory": "allow"
},
"targets": "auto",
"doctor": {
"failOn": "warning"
}
}
Python projects
depcurfew configures uv, Poetry, PDM, Pipenv and pip from the same depcurfew.json, and the bots for Python packages: Dependabot pip and uv entries and Renovate.
- Detection. A Python tool counts as used when its lockfile is there, when
pyproject.tomlhas its table, or when its own configuration file or a CI setup step shows it;requirements.txtmeans pip.depcurfew explainlists what it found. - Package names. Python packages on the policy's package lists (the cooldown exceptions,
installScripts.allowandinstallScripts.deny) take the prefixpypi:, for example"pypi:docopt". Names without a prefix stay npm names, so an exception for an npm package never applies to a Python package of the same name. Ruby gems, JSR packages, Rust crates and NuGet packages have no names on these lists, since no setting depcurfew writes takes one: an entry such as"gem:rails"or"cargo:serde"is an error that says to remove it, and an entry with another prefix is an error that lists the prefixes. - Install scripts are builds. A wheel installs without a build step, so no build code runs; a source distribution, a git repository or a local directory is built, which runs its build code. Forbidding builds does not keep an installed package's own code from running later, whenever Python imports it.
installScripts.default: "deny"forbids building, and the packages ininstallScripts.alloware the exceptions. None of the three tools can only allow a build, so these packages are always built from source, even where a wheel exists;applysays so, anddoctorlists the packages in the lockfile that would be built. Packages ininstallScripts.denymust not be built:doctorreports one that a tool would still build (Poetry, PDM, pip and Pipenv build git, local and URL dependencies whatever their settings say), and one without wheels that cannot be installed while building is blocked. - pip reads no project configuration.
applywrites the build options intorequirements.txt. The release-age delay cannot live in the project, soapplyprints the environment variable to set where pip runs. pip reads the options of the files a requirements file includes with-rand-c, and the options after a requirement on its line, into the same install, so an option there that makes pip build packages from source again (a package name or:all:for building from source,--only-binary=:none:) undoes--only-binary=:all::applycomments such option lines out in the included files inside the repository (they come back when the policy stops forbidding builds), and warns with the file and line where it cannot (build-switch-undonefor an option after a requirement or next to other options,requirements-include-unreadfor a URL, a path outside the repository or a missing file).doctorreports them (config-drift, andunreadable-configfor an include it cannot read), and follows the options in pip's order when it decides whether building is forbidden. - Lock again after
apply.uv.lockrecords the release-age delay, and PDM checkspdm.lockagainst all of its resolution settings, the delay and the build ban included. Afterapplychanges them,uv sync --lockedanduv lock --checkfail until you runuv lock, andpdm lock --checkandpdm install --checkuntil you runpdm lock;applysays so (lockfile-out-of-date), and so doesdoctor(the rulelockfile-out-of-date) while the lockfile is not locked again: it compares whatuv.lockrecords with the settings as uv does, and the hash inpdm.lockwith the one PDM computes frompyproject.toml.poetry.lockandPipfile.lockdo not depend on the settingsapplywrites. - PDM and Pipenv have no setting that pins their own version. Where nothing pins it,
applyconfigures for the latest version depcurfew knows and names that version (version-not-pinned). What CI jobs and Dockerfiles install counts as a version, anddoctorchecks it (manager-version-too-old): theversioninput ofpdm-project/setup-pdm(headnames none);pdm==<version>,pipenv==<version>,uv==<version>andpoetry==<version>inpip install,pipx installoruv tool install, with a variable read from theenvof the job or theENVandARGof the stage (pip install "poetry==${POETRY_VERSION}"); and the uv of the official uv image a Dockerfile copies from or starts from (COPY --from=ghcr.io/astral-sh/uv:0.8.22 /uv /uvx /bin/,FROM ghcr.io/astral-sh/uv:0.8.22-python3.12-bookworm;latestand a base name alone name no version). Pipenv takes the delay in whole days only (rounded up), has no exceptions and no project setting that forbids building;applysays what to set where Pipenv runs instead.doctorreadspdm.lockandPipfile.lock, and reports settings that turn the delay or the build ban off without a word: Pipenv's pure-Python resolver and PDM's uv mode. - Hatch has no settings of its own. Its environments install with uv or pip, so a Hatch project is covered by the uv configuration (add
"uv"totargetswhen its environments use the uv installer) or by the environment variables of pip. - Running depcurfew. depcurfew is a Node.js program; a Python project without
package.jsonruns it as In a repository without package.json describes.
In a repository without package.json
depcurfew is a Node.js program, and a repository without package.json in its root, such as a Rust, .NET, Python, PHP or Ruby project, does not need one to run it.
In GitHub Actions, the GitHub Action needs nothing installed.
Once, by hand, run an exact release with npx:
npx --yes depcurfew@1.5.2 doctor
Name a release that is at least as old as cooldown.minimum of the policy: depcurfew's own Claude Code hook denies a younger one, and so does npm where its min-release-age is set (npx then fails with ETARGET). The example names the newest release that was at least 7 days old when this README was written, usually the one before the newest. Without --yes, npx asks before it downloads a package the project does not have, and without a terminal or in CI it downloads without asking.
Pinned in the repository, keep depcurfew in the tools directory .claude/depcurfew: its own package.json pins depcurfew exactly, its package-lock.json is committed and .claude/depcurfew/node_modules is git-ignored. npm ci installs what that package-lock.json locks, and --prefix runs it in the tools directory, so both commands run from the root of the repository, without cd:
npm ci --ignore-scripts --prefix .claude/depcurfew
node .claude/depcurfew/node_modules/depcurfew/dist/cli.js doctor
doctor reads this copy too: depcurfew-outdated says when the depcurfew that runs is older than it, and the Claude Code hook can run it. To be offered new releases of the pin, you can add a Dependabot npm entry for the directory /.claude/depcurfew; apply gives it the cooldown of the policy like any other npm entry. The tools directory and the Action pin their versions separately, so update both.
Supported tools
| Tool | Release-age delay | Exceptions | Install-script allow-list (Python: packages built from source; Composer: plugins) | Dependency sources | Verified |
|---|---|---|---|---|---|
| uv | exclude-newer in uv.toml, pyproject.toml (duration string), since 0.9.17 | exclude-newer-package, since 0.9.25 | no-binary-package in pyproject.toml | none | 2026-10-30 – 2026-11-09 |
| pip | uploaded-prior-to (days) on the command line or in the environment, no project file, since 26.1.0 | none | --no-binary in requirements.txt | none | 2026-10-30 – 2026-11-11 |
| Poetry | solver. in poetry.toml (days), since 2.4.0 | solver., since 2.4.0 | installer. in poetry.toml | none | 2026-10-30 – 2026-11-03 |
| PDM | exclude-newer in pyproject.toml (duration string), since 2.26.9 | exclude-newer-override, since 2.29.1 | no-binary in pyproject.toml | none | 2026-10-30 – 2026-11-03 |
| Pipenv | cool-down-period in Pipfile (duration string), since 2026.6.2 | none | none | none | 2026-10-30 – 2026-11-11 |
⚠️ official sources disagree (depcurfew takes the safer reading) · ❓ official sources are incomplete. Every value with its source: docs/knowledge.md.
Protecting the whole machine
| Tool | User file | Settings apply --user writes (versions that read them there) | Needs the exact version |
|---|---|---|---|
| uv | Linux, macOS: $XDG_CONFIG_HOME/ (when it is an absolute path), else ~/.Windows: %APPDATA%\uv\uv., else ~\AppData\Roaming\uv\uv. | exclude-newer (0.9.17+), exclude-newer-package (0.9.25+), no-build (0.2.12+), no-build-package (0.2.12+), no-binary-package (0.2.12+) | yes |
| pip | Linux: $XDG_CONFIG_HOME/, else ~/.macOS: ~/ (when the folder exists), else ~/.Windows: %APPDATA%\pip\pip., else ~\AppData\Roaming\pip\pip.26.2.0+: Linux: $XDG_CONFIG_HOME/, else ~/.macOS: $XDG_DATA_HOME/ (when the folder exists), else ~/ (when the folder exists and $XDG_DATA_HOME is not set), else ~/.Windows: %APPDATA%\pip\pip., else ~\AppData\Roaming\pip\pip.26.3.0+: Linux: $XDG_CONFIG_HOME/ (when it is an absolute path), else ~/.macOS: $XDG_DATA_HOME/ (when it is an absolute path and the folder exists), else ~/ (when the folder exists and $XDG_DATA_HOME is not an absolute path), else ~/.Windows: %APPDATA%\pip\pip., else ~\AppData\Roaming\pip\pip. | uploaded-prior-to (26.1.0+), only-binary (7.1.0+), no-binary (7.1.0+) | yes |
| Poetry | 2.0.0+: Linux: $XDG_CONFIG_HOME/, else ~/.macOS: ~/Windows: %APPDATA%\pypoetry\config., else ~\AppData\Roaming\pypoetry\config.macOS with platformdirs before 4.6.0: ~/macOS with platformdirs 4.6.0–4.9.6: $XDG_CONFIG_HOME/, else $XDG_DATA_HOME/, else ~/macOS with platformdirs 4.9.6–4.11.8: $XDG_CONFIG_HOME/, else ~/macOS with platformdirs 4.11.8+: $XDG_CONFIG_HOME/ (when it is an absolute path), else ~/ | solver. (2.4.0+), solver. (2.4.0+), installer. (2.0.0+), installer. (1.2.0+) | no |
| PDM | Linux: $XDG_CONFIG_HOME/, else ~/.macOS: ~/Windows: ~\AppData\Local\pdm\pdm\config.macOS with platformdirs before 4.6.0: ~/macOS with platformdirs 4.6.0–4.9.6: $XDG_CONFIG_HOME/, else $XDG_DATA_HOME/, else ~/macOS with platformdirs 4.9.6–4.11.8: $XDG_CONFIG_HOME/, else ~/macOS with platformdirs 4.11.8+: $XDG_CONFIG_HOME/ (when it is an absolute path), else ~/ | strategy. (2.27.0+) | yes |
| Pipenv | none | none | no |
More: Protecting the whole machine.
doctor rules for these tools
python-config-ignored
error · A Python tool ignores a setting where it is written.
uv reads uv.toml instead of [tool.uv] when both exist, and in a workspace only the root; pip reads no configuration from the project (a pip.conf counts only where PIP_CONFIG_FILE names it) and rejects options it does not know in requirements files, so a CI job or a Dockerfile stage of the python image installs without the release-age delay unless it sets PIP_UPLOADED_PRIOR_TO or passes --uploaded-prior-to; PDM in uv mode installs without only-binary; Pipenv with resolver_backend (or its older name resolver) set to "pure-python" or "uv" locks without cool-down-period. A setting in such a place looks effective but is not, and an exclude-newer date freezes dependencies instead of delaying new versions.
Fix: Move the setting to the file the tool reads.
lockfile-out-of-date
warning · A uv.lock or pdm.lock was locked with other settings than the project has now.
uv records the release-age delay (exclude-newer and exclude-newer-package) in uv.lock, and PDM checks pdm.lock against a hash of the dependencies, the sources and the [tool.pdm.resolution] table, the delay and the build ban included. After a change, by depcurfew apply or by hand, uv sync --locked, uv lock --check, pdm lock --check and pdm install --check fail until the lockfile is locked again, and the lockfile still holds what was resolved under the old settings.
Fix: Lock again (uv lock or pdm lock) and commit the lockfile.
Every rule: doctor rules.
Sources
Every fact behind these tables, with its official source, is in the knowledge base: uv, pip, Poetry, PDM, Pipenv.