marinate-cli
v0.4.0
Published
npm outdated, but only for packages that have had time to age safely
Maintainers
Readme
marinate
npm outdated, but only for packages that have had time to age safely.
Supply chain attacks on npm registries typically sit for only a few hours before being caught and pulled. Marinate wraps npm outdated and enforces a minimum aging period (default: 24 hours) before considering a package version safe to upgrade to. If you wait a day, you dodge most drive-by compromises automatically.
Install
npm install -g marinate-cliOr run directly:
npx marinate-cliRequires Node.js >= 18. Zero external dependencies.
Usage
marinate [options]Package Current Wanted Latest Published Age Status
@types/react 19.2.13 19.2.13 19.2.14 2026-05-09 14:22 5d 2h ready
@types/react-dom 19.2.2 19.2.2 19.2.3 2026-05-13 22:01 16h marinating (8h left)
react 19.2.5 19.2.5 19.2.6 2026-05-12 17:30 1d 21h ready
react-router-dom 7.15.0 7.15.0 7.15.1 2026-05-14 11:05 3h marinating (21h left)
2 ready, 2 marinatingPackages are either ready (published >= threshold ago) or marinating (still in the cool-down window).
Options
| Option | Default | Description |
| -------------------------- | ----------- | --------------------------------------------------------------------------------------------- |
| --threshold <hours> | 24 | Minimum age in hours for a release to be considered safe |
| --json | | Emit JSON instead of a formatted table |
| --only-ready | | Show only packages ready for upgrade |
| --only-marinating | | Show only packages still in cool-down |
| --strict | | Exit code 1 if any packages are still marinating |
| --no-install | | Read package.json directly instead of running npm outdated (works without node_modules) |
| --update | | Update ready packages and pin all versions exactly in package.json |
| --update-target <target> | latest | Which version to write: latest or wanted |
| --dry-run | | Preview what --update would change without writing |
| --concurrency <n> | 8 | Number of parallel registry fetches |
| --registry <url> | npm default | Override registry endpoint |
| --no-color | | Disable ANSI color output |
| -h, --help | | Show help |
Unknown flags are forwarded to npm outdated, so --workspace and --workspaces work as expected.
Examples
# CI gate: fail if any dependency was published less than 48 hours ago
marinate --threshold 48 --strict
# Automated update: bump ready packages and preview the diff
marinate --update --dry-run
# Works in CI without node_modules
marinate --no-install --json
# Machine-readable output for scripting
marinate --json --only-readyHow --update works
When you pass --update, marinate rewrites package.json in two passes:
- Ready packages get their version bumped to the latest (or wanted) release, pinned exactly.
- All remaining dependencies have their range prefixes (
^,~,>=, etc.) stripped, so every version inpackage.jsonis pinned exactly.
This ensures the package manager installs precisely the versions marinate verified as safe -- no surprise resolutions via semver ranges. File formatting is preserved. It does not run npm install -- you decide when to install.
Exit codes
| Code | Meaning |
| ---- | --------------------------------------------------------------- |
| 0 | No actionable findings, or --strict not set |
| 1 | Marinating packages found with --strict; or invalid arguments |
| 2 | Network or parse error |
What if my current package is malicious?
Marinate protects you from upgrading to a malicious release. But what if your current dependency is compromised and a fix was just published minutes ago? Marinate would flag the fix as "marinating" and block it.
In that case, use the --emergency flag to bypass the cool-down for that specific package:
marinate --update --emergency --package <package-name>Warning:
--emergencyskips the cool-down window entirely (internally--threshold 0). Marinate will ask you to confirm before proceeding. This flag requires--package-- you cannot apply it globally to all dependencies at once.
A general workflow for responding to a compromised dependency:
- Find the CVE -- search for the CVE identifier on nvd.nist.gov, cve.org, or
npm auditto confirm the vulnerability, affected versions, and fixed version. - Cross-check the fix -- verify the patched version on npm advisories, Socket, or Snyk to make sure the fix is legitimate and from the expected maintainer.
- Update immediately -- run
marinate --update --emergency --package <package-name>and confirm the prompt to pick up the fix without waiting. - Run
npm installand verify your build. - Resume normal marination -- subsequent runs without
--emergencywill enforce the cool-down window again.
The --emergency escape hatch exists because security is about trade-offs: the cost of staying on a known-bad version is higher than the risk of adopting an unaged fix. Requiring an explicit package name and confirmation ensures you don't accidentally bypass the safety window for your entire dependency tree.
Claude Code hook
When an AI coding agent like Claude Code adds a dependency, it edits package.json (or runs npm install <pkg>) directly -- skipping the cool-down a human gets by running marinate first. The marinate hook command wires the gate into the agent's own edit loop so everything it adds is marinated (aged past the threshold) and sticky (pinned to an exact version).
marinate hook install # register the hooks in ./.claude/settings.json
marinate hook status # show whether the hooks are installed (exit 1 if not)
marinate hook uninstall # remove only the marinate-owned hook entriesinstall writes two cooperating hooks into settings.json (idempotent -- re-running updates the existing entries, and unrelated hooks are left untouched):
PostToolUseonEdit|Write|MultiEdit-- after the agent edits apackage.json, marinate diffs the added/changed dependencies, strips range prefixes to exact pins, and downgrades any still-marinating addition to the newest release that has aged past the threshold. It reports each rewrite back to the agent.PreToolUseonBash-- before any shell command runs, marinate inspects it. A one-shotnpm install <pkg>/yarn add/pnpm add/bun addis blocked (it would fetch and run a fresh package immediately), with feedback naming the marinated version to pin instead. A barenpm installis allowed only once the manifest is fully marinated-and-sticky.
Both hooks call marinate hook run internally, so the logic versions with the CLI -- there are no standalone scripts to copy or keep in sync.
| Hook flag | Default | Description |
| ----------------- | --------- | --------------------------------------------------------------------- |
| --project | (default) | Target ./.claude/settings.json (committed, protects the whole team) |
| --global | | Target ~/.claude/settings.json (every project on this machine) |
| --threshold <h> | 24 | Cool-down baked into the installed hooks |
| --fail-open | | On a registry/network error, warn and allow (default: fail closed) |
| --dry-run | | Print the settings.json that install/uninstall would write |
Why not npm audit / Snyk / Socket?
Those tools analyze published code for known vulnerabilities. Marinate doesn't look at code at all -- it only looks at time. A package published 10 minutes ago hasn't been reviewed by anyone yet, regardless of what static analysis says. Marinate buys you that review window. Use it alongside your existing security tools, not instead of them.
License
MIT
