From carl-tools
Dependency safety scan. Audit one or many projects/repos for SHA/digest pinning and release cooldowns across Docker, GitHub Actions, npm/pnpm/yarn/bun/deno, Python (uv/pdm/poetry/pip), .NET (NuGet), Rust (Cargo), Go, Java (Maven/Gradle), Ruby (Bundler), Terraform — and any other ecosystem encountered. Report violations, then apply fixes and add cooldowns only with the user's OK. Triggers on "dependency pinning", "pin deps", "supply chain", "cooldown", "are my deps safe".
How this skill is triggered — by the user, by Claude, or both
Slash command
/carl-tools:dependency-pinningThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A dependency **safety** scan — not a CVE scan. It checks two things across every
A dependency safety scan — not a CVE scan. It checks two things across every ecosystem in scope:
Report first. Mutate only on explicit approval. Bumping a pin is itself risky, so every proposed change names the new SHA and flags it for a supply-chain check.
Ask what to scan if unclear: a single project, a list of repos, or "everything under
~/dev/work". Then discover dependency manifests:
ROOT="${1:-.}"
/usr/bin/find "$ROOT" -type d \( -name node_modules -o -name .git -o -name .terraform \
-o -name vendor -o -name packages -o -name bin -o -name obj -o -path '*/assets/libs' \) -prune -o \
-type f \( \
-iname 'Dockerfile*' -o -name 'docker-compose*.y*ml' -o -name 'compose*.y*ml' \
-o -path '*/.github/workflows/*.y*ml' \
-o -name 'package.json' -o -name 'deno.json*' \
-o -name 'pyproject.toml' -o -name 'requirements*.txt' \
-o -name '*.csproj' -o -name 'packages.config' -o -name 'Directory.Packages.props' \
-o -name 'Cargo.toml' -o -name 'go.mod' \
-o -name 'pom.xml' -o -name 'build.gradle' -o -name 'build.gradle.kts' \
-o -name 'Gemfile' -o -name '*.gemspec' \
-o -name '*.tf' \
\) -print 2>/dev/null
# Also catch images pulled from scripts/CI, not just Dockerfiles/compose:
grep -rEn 'docker (run|pull|build)[^|]*[a-z0-9./-]+:[a-z0-9._-]+' "$ROOT" \
--include='*.sh' --include='*.y*ml' --include='Makefile' 2>/dev/null
Ignore vendored/minified asset trees (*/assets/**, *.min.*) — those package.json
files are bundled libraries, not your declared dependencies.
FROM repo/img@sha256:<digest> · image: repo/img@sha256:<digest>:latest, floating tags, no digest.grep -rEn '^\s*FROM |image:\s' <files> → flag any ref without @sha256:.
Also scan scripts/CI/Makefiles for docker run|pull|build ... img:tag — images
pulled outside Dockerfiles/compose are easy to miss and just as swappable.docker buildx imagetools inspect repo/img:<ver> --format '{{.Manifest.Digest}}'
then write repo/img@sha256:<digest> # <ver>.uses: owner/repo@<40-hex-sha> # vX.Y.Z@v4, @main, @<branch>.grep -rEn 'uses:\s' .github/workflows → flag any ref not matching a 40-char hex SHA.git ls-remote https://github.com/<owner>/<repo> refs/tags/<ver> → SHA; pin with the version in a trailing comment.ratchet (sethvargo/ratchet) or pinact pin+update actions by SHA;
Renovate/Dependabot minimumReleaseAge / cooldown gates the version that gets pinned.package-lock.json / yarn.lock / pnpm-lock.yaml /
bun.lock / deno.lock) with integrity hashes, and CI installs frozen
(npm ci, pnpm i --frozen-lockfile, yarn --immutable, deno install --frozen).^/~/latest without a lock; npm install in CI.minimumReleaseAge (config/.npmrc). For npm/yarn/bun,
add Renovate minimumReleaseAge: "7 days" (or Dependabot cooldown). Deno: pin exact
versions in import map / deno.json; deno.lock carries integrity.uv.lock, pdm.lock, poetry.lock) and
installed locked (uv sync --locked, pdm sync, poetry install); for bare pip,
hashed pins via pip-compile --generate-hashes + pip install --require-hashes.>=/* requirements, no lockfile, no hashes.pyproject.toml/requirements.txt present but no lockfile/hashes; ranges without a lock.exclude-newer / UV_EXCLUDE_NEWER (resolve as of a date —
a true cooldown). Otherwise Renovate minimumReleaseAge.packages.config version="x.y.z", or <PackageReference Version="x.y.z"/> with no range/*) plus a committed packages.lock.json
(enable <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>, restore
with dotnet restore --locked-mode). The lockfile carries contentHash per package.Version="*", Version="[1.0,)"), no lockfile, packages
restored from a non-pinned feed.grep -rEn 'Version="\*"|Version="\[' **/*.csproj; .csproj/packages.config
present but no packages.lock.json beside it; check Directory.Packages.props for
central versions if used.packages.lock.json; pin the feed in
nuget.config.minimumReleaseAge (NuGet supported).Terser, but the same two questions apply (immutable pin? cooldown?):
Cargo.lock (carries crates.io sha256 checksums); cargo --locked.
No native cooldown → Renovate minimumReleaseAge (cargo datasource). cargo audit/cargo vet adjacent.go.mod + go.sum (cryptographic hashes, verified vs the checksum DB);
go mod verify, build with -mod=readonly. No native cooldown → Renovate (gomod).pom.xml — ban LATEST/RELEASE/version ranges
(enforce with maven-enforcer requireReleaseDeps / ban-dynamic-versions). No lockfile natively.gradle.lockfile) + verification metadata
(gradle --write-verification-metadata sha256 → verification-metadata.xml with checksums/signatures).Gemfile.lock; bundle config set frozen true; add checksums with
bundle lock --add-checksums (Bundler 2.5+). No native cooldown → Renovate (bundler).minimumReleaseAge /
Dependabot cooldown, which support these datasources.Don't stop at the languages listed here. For any package manager in the target:
latest version specifiers in the manifest.minimumReleaseAge, exclude-newer, --as-of, min-age, quarantine),
then a meta-tool (Renovate minimumReleaseAge / Dependabot cooldown) for that
datasource. If unsure, check the tool's current docs rather than guessing..terraform.lock.hcl committed with multi-platform hashes
(terraform providers lock -platform=linux_amd64 -platform=darwin_arm64 ...);
providers pinned in required_providers; modules pinned by version or git commit SHA;
AMIs/data sources pinned by ID (no most_recent = true).most_recent = true, modules on main..gitignore; grep -rn 'most_recent\s*=\s*true' *.tf; module sources on a branch."minimumReleaseAge": "7 days" in renovate.json — the
single best lever; gates npm/pip/docker/actions/etc. behind the cooldown.cooldown: block in .github/dependabot.yml.minimumReleaseAge · uv: exclude-newer · GH Actions: ratchet/pinact.DEPENDENCY SAFETY REPORT (cooldown target: 7 days)
===================================================
<repo/path>
Docker : 2 images unpinned (FROM node:20, postgres:16) — PIN MISSING
GH Actions : 3 actions on tags (@v4) — PIN MISSING
JS (pnpm) : lockfile committed ✓ · no cooldown — COOLDOWN MISSING
Python (uv) : uv.lock ✓ · exclude-newer not set — COOLDOWN MISSING
Terraform : .terraform.lock.hcl gitignored — LOCK NOT COMMITTED
...
SUMMARY: 4 pin gaps, 3 cooldown gaps, 1 uncommitted lock across N repos.
Classify each as: OK · PIN MISSING · COOLDOWN MISSING · LOCK NOT COMMITTED.
Then list exact fixes (the resolved SHA/digest, the config snippet) per item.
:latest/floating ref as a "fix".Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub carlkibler/agent-skills --plugin ralph-loop