AGENT SKILL

dependency-currency

Periodic maintenance pass over a project's dependencies, tooling, pins and agent config: find what is unused, vulnerable, broken or misdescribed, upgrade what is safe, and name what needs a human. Use for 'update the dependencies', 'tooling update', 'are we current', 'check for outdated packages', a scheduled maintenance run, or before a release. Works in any project — Python, Node, or both — by reading the project's own AGENTS.md for its check commands rather than assuming a stack.

License 许可
Apache-2.0
最近更新
2026-09-29
Stars
★ 0
Forks
0

01 前言 Front Matter

name
dependency-currency
description
Periodic maintenance pass over a project's dependencies, tooling, pins and agent config: find what is unused, vulnerable, broken or misdescribed, upgrade what is safe, and name what needs a human. Use for 'update the dependencies', 'tooling update', 'are we current', 'check for outdated packages', a scheduled maintenance run, or before a release. Works in any project — Python, Node, or both — by reading the project's own AGENTS.md for its check commands rather than assuming a stack.

02 资源文件 9

03 SKILL.md 正文

Dependency currency — a maintenance pass that is not a bump list

<!-- agent-sync:start dependency-currency --> Vendor-neutral and machine-wide: this lives in ~/.agents/skills/, which Codex, Grok and Kilo read themselves; Claude Code gets a one-line pointer to it. It is written to be followed by any coding agent — Codex, Grok, Claude, Kilo, Cursor, Gemini, Copilot — or by a person. It names no tool of its own beyond the package managers a project already uses, and every check command it runs is one the project already defines.

Start here: "what is outdated" is the wrong first question

In a maintained project, version currency is usually the least informative signal: most packages are at or one patch behind latest, and the findings that matter are dependencies nothing imports (which can carry every CVE the audit reports), pin comments that no longer match reality, and lockfile commands that silently remove packages. Leading with outdated produces a list of patch bumps and misses all of that.

So the order is: unused → vulnerable → misdescribed → outdated. Currency is the last question, not the first.

0. Learn the project before touching it

Read the project's AGENTS.md (or its README.md where it has none) and find:

  • The check commands. Never assume. Observed across three sibling repos: one runs ruff check src tests then pytest; one runs a single scripts/gate.sh that wraps import-linter and architecture tests; one runs uv run ruff check ., strict mypy, and cd ui && npm run typecheck. A skill that hardcodes pytest is wrong in two of the three.
  • Which pins are deliberate. A pin with a comment is a decision someone made. You are going to verify that comment, not obey it and not ignore it.
  • What the project says it does not check. Do not "fix" it by adding a check the project deliberately refused.

Record the baseline: run the project's checks before changing anything. A pass that cannot say "1041 tests passed before and after" cannot claim it changed nothing.

1. Unused — what is declared but never runs

The highest-value step and the one a bump list skips entirely.

For each declared dependency, ask whether anything actually reaches it:

grep -rn "<import-name>" --include=<source-glob> .

A hit count of zero is a candidate, not a verdict. Before removing anything, all three of these must hold:

  1. No direct import anywhere in source or tests.
  2. Nothing in the lockfile requires it. Read reverse-dependencies out of the lock. This is not optional and it is the one that bites: in repo B the same "zero direct imports" signal that correctly condemned five libraries would also have removed statsmodels, which is reached through statsforecast and arch and would have broken the ML package.
  3. No dynamic reference. Check importlib, plugin registries, entry points, and config files that name a module as a string. A grep for import does not see importlib.import_module(name) or a "module:Class" entry in a TOML.

Removal needs a stronger proof than addition. Adding a dependency that turns out to be unnecessary wastes disk; removing one that turns out to be reachable breaks the product, often not at import time but on the one code path that uses it.

Two patterns worth hunting directly:

  • A module whose name matches a dependency but does not import it. Someone wrote the replacement and never removed the dep. Repo B had two: experiment.py hand-rolling what mlflow would do, tearsheet.py delegating to an internal metrics module where quantstats would sit.
  • A dependency that no longer imports cleanly. If nothing imports it, nothing has noticed it broke. Repo B's vectorbt raised ValueError on import against the resolved plotly — it would have failed the moment anyone tried to use it. Try importing each candidate before deciding.

2. Vulnerable — and where the vulnerability actually lives

Run the ecosystem's audit: pip-audit, npm audit, cargo audit, whatever the project's language provides.

Then ask the question the audit does not: is the vulnerable code reachable? A CVE in a library nothing imports is a declaration problem, not a security problem, and the fix is removal rather than an upgrade. Repo B's 53 vulnerabilities were 51 in mlflow and 2 in pyarrow, where pyarrow was only present as mlflow's transitive dependency, and nothing imported either. Removing one declaration took the count to zero.

Report both numbers: how many vulnerabilities, and how many are reachable.

3. Misdescribed — where a comment disagrees with reality

Every pin comment is a claim, and claims decay. Verify them. This is where both passes found their sharpest results.

Repo A pinned onnxruntime-gpu>=1.20,<1.23 with a comment saying "1.23+ wants CUDA 13". The real boundary was 1.27 — wrong by four minor versions, and four versions of fixes were being refused for no reason. Verified two ways rather than read: the package metadata showed 1.23–1.26 still requiring nvidia-cudnn-cu12 and 1.27 switching to -cu13, and in a throwaway environment 1.26 created a real GPU session and ran a kernel while 1.27 failed at import with libcudart.so.13: cannot open shared object file.

Note the shape: the pin was real — there is a genuine wall — but it was in the wrong place. Neither "trust the comment" nor "delete the pin" was correct.

Also check documentation that points at a library. Repo B's docs listed a dependency as the way to do a thing; that library's author died in early 2024, it is unmaintained, and a maintained fork is the drop-in replacement. The doc would have sent the next person to a dead library.

Never bump a pin whose comment you have not verified. If you cannot verify it, say so and leave the pin alone.

4. Outdated — last, and read the right column

Only now ask what is behind.

Read the version numbers, not the exit code and not the "wanted" column. npm outdated exits 0 and prints nothing when every dependency satisfies its declared range — but a ^18 range is satisfied forever while React reaches 19. In repo B, react, typescript and tailwindcss all showed Current == Wanted with Latest one or more majors ahead. The information was there, in the Latest column; an agent that checked the exit code or diffed Current against Wanted would have concluded "current" while five majors behind.

The equivalent for a lockfile-based resolver: run the upgrade in dry-run and read every line, because the destructive ones are formatted like the safe ones:

Resolved 60 packages
Remove coloredlogs v15.0.1            <- these
Update filelock v3.32.6 -> v4.0.0
Update onnxruntime-gpu v1.22.0 -> v1.26.0
Remove sympy v1.14.0                  <- and these

Two specific traps, both observed:

  • A "Update" line that goes backwards. Repo B saw ccxt 4.5.76 -> 4.5.64 presented among the updates — a downgrade wearing the word Update. Diff proposed against installed and flag any version that decreases, whatever the label next to it says.
  • A removal count above zero on an upgrade run is a stop-and-read, never a proceed. Repo A's plain sync would have removed 60 packages including the speech engine, the voice and the cryptography library; repo B's removed 92. Both commands "succeed" and leave a process that still starts, still serves, and has quietly lost a capability. That failure mode — green, running, silently degraded — is worse than a crash.

The pipe that manufactures a clean result

Worth its own rule, because it silently invalidates every check this skill runs, not just the dependency ones.

$? after a pipeline is the last command's status. So a check piped into head, tail or grep — which is how an agent keeps output readable — reports the filter's success and discards the tool's verdict. Measured on one machine, three tools, same shape:

npm outdated  >/dev/null           -> 1     |  … | head -3  -> 0
pytest -k nothing-matches          -> 5     |  … | tail -1  -> 0
ruff check <file-with-an-error>    -> 1     |  … | head -2  -> 0

All three are real failures reported as success. pip-audit, npm audit, mypy and most linters signal the same way, so an agent that runs checks through a pager and reads the status will pass a failing suite.

Use set -o pipefail, or do not read $? through a pipe at all — run the command bare for its status and separately for its output.

This is how the npm finding above was originally misreported: the exit code came back 0 through a | head, and that one-line summary was believed over eleven rows of contradicting version output printed directly above it.

The general rule: when a one-line summary disagrees with the output above it, doubt the summary. A tool that prints eleven rows of versions and then reports clean is not clean. This is the same failure shape as a benchmark that could not have shown the problem being trusted because it returned — see step 6.

The silent case, which has no backstop

The rule above relies on there being output to disagree with. Usually there is, and it is what saves you: measured across pytest's failure modes, three of the four still print something a filter catches even though the exit code lied.

pytest <a failing test>   bare 1  piped 0   prints "FAILED …" and "1 failed"
pytest <an import error>  bare 2  piped 0   prints "ERROR <file>"
pytest <a bad path>       bare 4  piped 0   prints "ERROR: file not found"
pytest -k <no match>      bare 5  piped 0   prints NOTHING

The first three are survivable by accident. The fourth is not: no tests were collected, so there are no failures to report, and a filter looking for FAILED, ERROR, passed or failed emits zero lines.

A check that produced no output is not a check that passed. Blank is the most reassuring possible result and here it means the suite never ran.

This is a live risk for exactly this skill. A maintenance pass narrows to "just the tests near what I changed" with a -k expression or a path — and a rename, a moved file or a renamed test makes that selector match nothing. The run exits 5, the filtered output is empty, and it looks identical to success.

So for every check, assert on the positive evidence rather than the absence of negative evidence: a test count that is non-zero and matches expectation, a named summary line, an explicit exit code read without a pipe. "Nothing was reported" is not a result.

5. Upgrade what is safe; name what is not

Upgrade by default where it is genuinely safe: patch and minor bumps of libraries the tests actually exercise, with the project's checks green before and after.

Do not upgrade on a version number alone when any of these hold:

  • It is load-bearing and native. GPU runtimes, CUDA/cuDNN stacks, compiled extensions, anything whose failure mode the test suite cannot see. Repo A's tests pass identically whether or not the GPU path works — a green suite is not evidence there.
  • It is a major with breaking changes and the area has no test coverage. Repo B deliberately left React 18→19 and Tailwind 3→4 alone because that UI has no meaningful tests to catch a regression, and said so. A named, unupgraded major is a better outcome than a broken one you bumped.
  • You have not verified the pin's comment. See step 3.

When you decline an upgrade, name it, say why, and say what would let it proceed — "needs a human clicking through the UI", "needs a CUDA 13 runtime on the machine". A declined upgrade with a stated unblock condition is useful; a silent omission is not.

If the repos disagree on a tool's version, do not "align" by downgrading the one that is ahead. A repo running a newer linter is not drifting, it is leading; the fix belongs in the laggards.

6. Measure, if you are changing a default

If the pass changes a behavioural default — a model, an algorithm, a compression level — measure it. And vary the thing you are judging.

Repo A changed a speech-to-text default and benchmarked it first on clean synthesised audio, which produced four near-identical transcripts and would have rejected the change. Only degrading the signal separated the models: at a 0 dB signal-to-noise ratio the word error rate was 43.2% against 28.9%. On clean audio the new default was marginally worse. The conditions the benchmark ran under decided the answer, and the first set of conditions was the wrong one.

State honestly what the measurement does not show. That same pass could not reproduce the original user complaint synthetically, and said so in the code rather than claiming a fix it had not demonstrated.

7. Check the agent-facing surface too

This is a tooling pass, not only a library pass. Also look at:

  • Skills, hooks and MCP servers — are they current, and is any of them generated from a source file that should be edited instead? Editing a generated file is work that disappears on the next run.
  • Install instructions that do not match reality. Repo A's README installed one extra while the working environment needed four; following the documented command produced a pipeline with no voice that stored transcripts in the clear.
  • Name collisions. Repo A's project shares its name with an unrelated package on the public index, so the standard "what is outdated" command reported the project itself as outdated against a stranger's package.

What to say at the end

Lead with what was not a version bump. Then:

  • Unused: what was removed, and the three checks that justified each.
  • Vulnerable: how many, and how many reachable.
  • Misdescribed: every pin comment or doc that disagreed with reality, with the evidence that settled it.
  • Outdated: what moved, what did not, and the unblock condition for each thing that did not.
  • Checks: the project's own commands, verbatim, before and after — and their real exit codes, not ones read through a pipe.
  • For a human: the majors you deliberately left, and what they would need.

If the pass found nothing, say that plainly and say what you checked. "Already current" is a real and useful result — it is most of what both reference passes found — and it is only trustworthy if it names the ground it covered.

Report the mechanism, not only the conclusion

Every rule in this skill about pipes and exit codes exists because of how one finding travelled between two agents, and the method is worth more than the findings.

One reported a conclusion — "the UI is five majors behind" — with a mechanism attached: "the tool prints nothing and exits 0". The conclusion was true and could not be checked by inspection. The mechanism was false and testable in one command. Running it found that the tool prints eleven lines and exits 1; chasing that gap found the real cause, an exit code eaten by a pipe, which was corrupting every check both agents ran and which neither had noticed.

The mechanism is the falsifiable part of a claim. Report it even when the conclusion is what matters, because it is what lets someone else catch you. A conclusion alone is unfalsifiable and travels unchallenged.

And when you cannot reproduce something a peer reported, the useful move is neither "they are wrong" nor "I must be missing something". Treat the discrepancy as its own finding and chase it. Verify a peer's evidence exactly as you would your own — not from distrust, but because a rule carrying a wrong mechanism discredits its true half the first time someone tests it.

Finally: re-verify anything you have already reported as green, bare rather than piped. "I already said it passed" is not evidence. <!-- agent-sync:end -->

04 同仓库更多 Skills

maxjbarfuss
maxjbarfuss/max_llm

train

Launch a new max_llm training run on both GPUs (RTX 4090 + RTX 3090 Ti) through torchrun DDP, resolving the TOML config by name or path. Use only when the user explicitly asks to start, launch or begin a run — "resume", "continue" or "keep training" mean the existing run; takes an optional config name or path.

★ 0
maxjbarfuss
maxjbarfuss/max_llm

serve-live

Before a person looks at a running service, make sure what is running IS the code in the checkout. Restart the services your change actually touched — not every service — and verify each is serving the current commit rather than merely answering; check the remaining dependencies are up without bouncing them. Use whenever you finish work a person will view in a browser or client, whenever you say a page or service is ready, after any commit that changes served code, and when asked to 'restart', 'refresh the services', 'is it live', or 'serve the latest'. Also covers waiting for lazy warm-up before calling a service ready.

★ 0
maxjbarfuss
maxjbarfuss/max_llm

review

Review a local change, branch, pull request, or stack for correctness, regressions, security, tests, and unnecessary complexity. Use when asked to review rather than implement.

★ 0
maxjbarfuss
maxjbarfuss/max_llm

regression-coverage

Reshape a slow or brittle test suite around observable user journeys and published contracts. Use when pruning tests, repairing weak smoke coverage, or addressing repeated feature regressions.

★ 0
maxjbarfuss
maxjbarfuss/max_llm

pr-babysit

Monitor selected pull requests and report or safely act on review, CI, mergeability, and stack state.

★ 0
maxjbarfuss
maxjbarfuss/max_llm

keel

Use a project's local keel code graph to assess change impact, find callers and reuse candidates, validate a plan, or check an edit. Applies only in a repository with a .keel/ directory; skip it for prose-only work.

★ 0