Skip to content

feat: add dependency and toolchain maintenance agent + skills - #223

Open
fedecor9 wants to merge 7 commits into
mainfrom
feat/dependency-updater-agent
Open

fedecor9 wants to merge 7 commits into
mainfrom
feat/dependency-updater-agent

Conversation

@fedecor9

Copy link
Copy Markdown
Contributor

What this is

A self-contained dependency- and toolchain-maintenance system for this repo,
built entirely as Claude Code configuration under .claude/ — no application
code changes. It's a dependency-manager agent orchestrating five skills
that each own one stage of a pipeline: audit what's outdated, prove an
update is actually safe before touching anything, apply it, keep the
pipeline's own docs in sync, and open a PR with the evidence as the
description.

It's built to be language/framework-agnostic: everything ecosystem-specific
(pub.dev URLs, OSV.dev's ecosystem name, Dart's version-suffix quirks) lives
in a small adapter file pair, not in the skills themselves. Adding a new
ecosystem later is one new .md + one new .json, no skill edited.

How it works

dependency-audit ─┐
                   ├─► dependency-update-audit ─► dependency-update-apply ─► dependency-doc-sync ─► dependency-update-pr
toolchain-audit  ──┘      (safety gate)              (apply the bump)         (fix stale docs)         (open the PR)
  (what's outdated?)   (prove it's safe)
  1. dependency-audit — read-only inventory. Runs a script
    (scripts/fetch_updates.py) once per manifest that checks what's
    outdated, queries the registry and security advisories, and fetches the
    relevant changelog excerpt — then grades each finding on two independent
    axes: urgency (security-driven — why you'd want this) and risk
    (breaking-change-driven — what it costs). A critical CVE fixed by a
    trivial patch is urgency: critical, risk: none — those aren't the same
    question.
  2. toolchain-audit — the same idea for the Flutter/Dart SDK, AGP,
    Kotlin, and the Gradle wrapper, kept separate since a toolchain bump's
    blast radius (native project files, CI config) is nothing like a package
    bump.
  3. dependency-update-audit — the safety gate. Applies the candidate on
    a throwaway scratch branch, runs ./scripts/checks.sh for real, and only
    then writes a verdict — "the changelog looked fine" is never accepted
    as evidence. Also runs a batch check when a tier has multiple candidates,
    since packages that are each safe alone can still conflict when bumped
    together.
  4. dependency-update-apply — applies whichever tier a human has
    explicitly approved, on its own branch, re-verifying for real before
    committing.
  5. dependency-doc-sync — after a bump lands, fixes any of the
    pipeline's own docs that just went stale (e.g. a toolchain adapter's
    hardcoded "current version" note).
  6. dependency-update-pr — opens one PR per safety tier (draft for
    anything with breaking changes), with the audit evidence as the PR body.

The agent never applies or opens a PR without an explicit human go-ahead per
tier — audit and safety-gate are the only stages that run unattended.

Example: input → output

Given this repo's real pubspec.yaml (flutter_secure_storage: ^10.3.1),
running dependency-audit produces (full worked example, including the
printed summary, in .claude/skills/dependency-audit/references/example-output.md):

{
  "package": "flutter_secure_storage",
  "current": "10.3.1",
  "upgradable": "10.3.4",
  "latest_compatible": "11.1.1",
  "bump": "major",
  "urgency": "maintenance",
  "risk": "medium",
  "tier": "review",
  "breaking_changes": [
    "11.0.0: items deprecated in v10 were removed; data saved with a deprecated algorithm becomes unusable unless the app already passed through v10 to migrate it.",
    "Android: removed KeyCipherAlgorithm.RSA_ECB_PKCS1Padding, StorageCipherAlgorithm.AES_CBC_PKCS7Padding, and the encryptedSharedPreferences/sharedPreferencesName AndroidOptions fields.",
    "Android: raised minSdk to 24 and compileSdk to 37."
  ]
}

which the skill renders as:

tier: review

package current → target urgency risk verdict
flutter_secure_storage 10.3.1 → 11.1.1 maintenance medium null (not yet audited)

10.3.4 is available with no manifest edit at all. 11.1.1 is available
if you're willing to take the breaking changes below — going from v10 to
v11 removes several deprecated Android cipher/options APIs, and the
changelog explicitly warns that skipping v10 on the way to v11 can make
previously-saved secure-storage data unreadable.

Note upgradable (10.3.4) vs latest_compatible (11.1.1) — the report
surfaces both a free, zero-risk bump and the further one that requires
accepting the breaking changes, rather than only showing whichever is
"furthest." Verified deterministic: two independent runs against the same
repo state produced byte-identical structured fields and near-identical
breaking_changes prose.

The scripts (why they exist)

Pushing deterministic work into small, stdlib-only Python scripts — rather
than having the agent hand-run and parse commands itself — fixes real bugs
found while testing this live: pub.dev's changelog is rendered HTML, not
markdown; pub outdated's output has quirks (a non-JSON banner prefix,
inconsistent fields); and free-form text edits to a manifest can silently
turn an exact pin into a range under pressure.

  • dependency-audit/scripts/fetch_updates.py — one call per manifest
    replaces dozens of individual registry/advisory/changelog calls. Batches
    the security-advisory check (OSV.dev) into a single request instead of one
    per package.
  • dependency-update-apply/scripts/bump_manifest.py — edits exactly one
    version number on one manifest line, preserving whatever pin style (exact
    or range) was already there. Refuses rather than guesses on any ambiguous
    match.
  • dependency-update-apply/scripts/bump_toolchain_file.py — the same
    discipline for toolchain files, which span three incompatible formats
    (.fvmrc's JSON, settings.gradle's Groovy DSL, a version embedded in a
    URL) that no single parser handles — so this does a literal, disambiguated
    substitution instead.

Status

Built and tested this session: the audit path (read-only) has been run live
against this repo's real dependencies multiple times, including two parallel
clean-context trial runs confirming consistent output. The safety-gate →
apply → PR path has been validated on isolated synthetic candidates, not yet
run end-to-end against a real multi-package tier. Opening as a draft for
that reason.

Adds a dependency-manager agent orchestrating a 5-stage pipeline
(dependency-audit / toolchain-audit -> dependency-update-audit safety
gate -> dependency-update-apply -> dependency-doc-sync ->
dependency-update-pr) under .claude/. Language/framework agnosticism is
achieved via per-ecosystem adapter files (Dart/Flutter shipped, npm/etc
can be added without editing any skill). Policy lives in
.claude/rules/dependencies.md.
Replaces per-package registry/advisory/changelog tool calls in
dependency-audit with a single stdlib-only Python script per manifest
(scripts/fetch_updates.py), fixing several real bugs found testing it
live: pub.dev's changelog is rendered HTML not markdown, pub outdated's
field shapes, SDK pseudo-dependencies colliding with unrelated manifest
lines, and no-lockfile manifests producing noisy no-op candidates.

Adds bump_manifest.py and bump_toolchain_file.py so version bumps are
applied as precise scripted edits instead of free-form text edits,
guaranteeing a pin's existing style (exact vs range) is preserved.

Adds an `upgradable` field so the audit reports both the zero-edit-cost
update and the further one requiring a pin change, when they differ.

Adds a batch-verification step to dependency-update-audit: after every
candidate in a tier passes individually, apply them all together on one
scratch branch to catch transitive conflicts a one-at-a-time check can't
see, attributing any failure back to the specific candidate(s) responsible
via the same verdict/evidence fields already used everywhere else.
Persists the candidates.json + printed-summary example (real
flutter_secure_storage/sqflite audit output) as a reference file instead
of only living in a PR description, so the target output shape stays
documented alongside the skill.
- Loosen the dependency-manager agent and toolchain-audit descriptions to
  lead with adapter-driven capability instead of naming Flutter as the
  only supported ecosystem.
- Replace the Claude-authorship line in PR bodies with a generic
  "generated by an automated agent" disclaimer.
- Rewrite the Flutter/Dart toolchain adapter to drop hard references to
  this repo's specific PR/branch/commit history (which won't exist if
  this pipeline is reused elsewhere), replacing the worked example with
  a self-contained illustrative one, and correct its stale SDK/AGP/
  Kotlin/Gradle version numbers to match the repo's actual current pins.
- Drop a stale OPEN QUESTION comment in the dart-flutter adapter about
  CLAUDE.md's git history, now resolved.
Comment thread .claude/agents/dependency-manager.md Outdated
- Generalize the remaining Flutter/repo-name hardcoding in the live
  .claude/agents/dependency-manager.md (this repo name, package-manager
  examples, toolchain component lists, never-auto-update package names,
  verification-command path) so it points at adapters/rule files instead.
- Add plugin/dependency-manager/: a self-contained Claude Code plugin
  bundling the agent and all six pipeline skills, with the remaining
  repo-specific script/CI assumptions (checks.sh, fastlane lane,
  pr-title-checker config) genericized to "whatever this repo's
  verification_cmd/adapter/rule file defines."
- Ship the Dart/Flutter ecosystem and toolchain adapters plus a filled
  dependencies.md as a bundled worked example under examples/, so a new
  Flutter repo has a running-start reference to copy.
- Add a local marketplace manifest (plugin/.claude-plugin/marketplace.json)
  and a plugin README covering structure, the adapter pattern, and
  install/adoption steps for a new repo.
@fedecor9
fedecor9 marked this pull request as ready for review September 21, 2026 15:56
…tion

- Add bundled ecosystem adapters for python-pip, python-poetry, python-uv,
  npm, yarn, and pnpm alongside the existing dart-flutter one, each a
  worked-example .md + machine-readable .json pair.
- Generalize scripts/fetch_updates.py, which previously hardcoded pub's
  exact outdated-command JSON shape (nested {"version": ...} fields, a
  literal "pubspec.yaml", YAML-only dependency section headers) and would
  not have actually worked against any of the new adapters. It now
  dispatches on a config-driven outdated_shape (pub / npm-style object /
  flat array / yarn's table envelope / poetry's array) and config-driven
  manifest_filename/manifest_section_headers, verified against fixture data
  for every shape plus an end-to-end offline run.
- Add two-location adapter resolution to every skill that loads one:
  .claude/dependency-manager/ecosystems|toolchains/<name> in the *target*
  repo first (repo-local override, or where a newly detected ecosystem's
  adapter gets drafted), falling back to this plugin's own bundled copy.
  Never write into ${CLAUDE_PLUGIN_ROOT} — it's a shared, read-only cache
  across every repo the plugin is installed in. New refusal rule in
  agents/dependency-manager.md codifies this.
- Update README.md and the ecosystem/toolchain _template.md files to
  document the bundled adapter list and the resolution/drafting mechanism.
Addresses PR review comment on .claude/agents/dependency-manager.md:3 —
reword "(Dart/Flutter via pub today; any ecosystem with an adapter)" so
it's clear the Flutter reference is this repo's current ecosystem, not a
hardcoded assumption, matching the phrasing already used in the plugin
package's copy of this file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant