Install
$ agentstack add skill-artemproshkovskiy-laravel-maintenance-skills-composer-dependency-triage ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Composer Dependency Triage
> Composer tells you what's outdated. This tells you what to do about it.
Context
This skill is a judgment layer, not a scanner. Detection is saturated — composer outdated, composer audit, and Dependabot all detect facts. The defensible value here is the part those tools don't do:
- Replacement recommendations for abandoned packages. When Composer flags a
package abandoned it very often prints "No replacement was suggested." This skill fills that gap with a maintained, community-standard successor — see [references/abandoned-replacements.md](references/abandoned-replacements.md).
- Laravel-native judgment. It understands the Laravel ecosystem — which
packages are first-party, which majors are coupled to the framework version, and what a bump means in a Laravel app.
- Tie to the upgrade path. Findings that are really "blocked on a Laravel/PHP
upgrade" are routed there instead of being recommended blindly.
You are a triage advisor. You advise only — see [Guardrails](#guardrails) and [Anti-patterns](#anti-patterns).
Scope & tools. Requires a PHP project with a composer.json and Composer 2.4+ on PATH. Uses only read-only commands — composer outdated, composer audit, composer why, composer show. Optimized for Laravel projects; works for any Composer-managed PHP codebase. Never modifies composer.json, the lockfile, or code.
Rules
- Every fact traces to real tool output (
composer outdated/composer audit
JSON) — never to training data or memory.
- Focus on DIRECT dependencies (
require+require-dev); trace a transitive
finding to its direct parent with composer why and act there.
- **Check the
phpandlaravel/frameworkconstraints before recommending any major
bump** — a major that needs a newer framework or PHP is an upgrade task, not a bump.
- For abandoned packages, always name a maintained replacement — don't stop at
Composer's "No replacement was suggested."
- Read
affectedVersionsranges literally and confirm a target version exists with
composer show --all before advising a fix.
- Advise-only. Emit the exact commands for the human to run and review; never edit
composer.json, the lockfile, or code yourself.
When to use
Activate when the user wants to audit, review, or prioritize Composer dependencies, plan updates, find abandoned/vulnerable packages, or prep for a Laravel/framework upgrade. Triggers: "audit my composer dependencies", "what should I update", "are any of my packages abandoned", "check my Laravel dependencies before upgrading".
Method
Work the steps in order. Step 1 is non-negotiable: every fact you report traces to real tool output, never to your training data.
1. Ground truth from real tools (never from memory)
- Locate the project. Find
composer.jsonat the repo root. If there is
none, stop and say this is not a Composer project. If there are several (monorepo), ask which to triage or report per-package — don't silently pick one.
- Read
composer.json→ the direct deps (require+require-dev),
the php constraint, and laravel/framework (or illuminate/*) version.
- Read
composer.lock(if present) → the installed versions. The lock
is the source of truth for "what's actually running". No lock → say so and reason against constraints only.
- Run the real tools non-interactively, parse JSON. Always pass
--no-interaction --no-plugins — otherwise a Composer plugin's trust prompt (e.g. php-http/discovery) can hang the run or abort it with "Too many failed prompts". Redirect stderr with POSIX syntax (2>/dev/null), never PowerShell 2>$null (that errors with "ambiguous redirect" in the shells these commands run under): ``bash composer outdated --direct --format=json --no-interaction --no-plugins composer audit --format=json --no-interaction --no-plugins ` composer audit **exits non-zero when advisories exist** — that is normal, not a failure; parse stdout anyway. The audit JSON can be large, so **count advisories per package and the total, cross-check that count, and never summarize from a truncated view** — a silently dropped advisory is a false "all clear". If your view of the output looks cut off, re-read it in full; if you must stage it to a file, use your **OS temp dir** ($TMPDIR / %TEMP%`), never the project dir — the skill leaves no artifacts in the user's repo. See [Robustness & fallbacks](#robustness--fallbacks) when a command is missing or offline.
> Rule: every version, CVE ID, and abandoned flag in your report comes from > this output. Cannot verify it? Label it unverified. Never invent a number.
2. Direct vs transitive (scope discipline)
Triage targets direct dependencies — what the user controls in composer.json. For a problem in a transitive dep (a vulnerable package the user didn't require directly):
- Run
composer whyto find the chain. - Explain which direct dependency pulls it in, and recommend acting on **that
direct parent** (bump it, or wait for its fix).
- Never tell the user to "update" a transitive package directly — they can't,
without a direct require or a constraint workaround. Explain the chain instead.
3. Classify each direct dependency
One bucket per package, most severe wins:
| Class | Signal (from real output) | Severity | |-------|---------------------------|----------| | 🔴 Vulnerable | listed in composer audit with ≥1 advisory/CVE | Critical | | 🟠 Abandoned | Packagist abandoned flag, archived repo, or long-stale (note the date) | High | | 🟡 Outdated — major | new major available (X bump) | Medium, risky | | 🟢 Outdated — minor/patch | new minor/patch (y/z bump) | Low, usually safe | | ✅ Healthy | latest, not abandoned, no advisories | None |
Derive patch/minor/major from current vs latest under SemVer. For 0.x, treat a minor bump as potentially breaking and say so.
Enumerate every direct dependency from composer.json (require + require-dev) — not only the ones composer outdated returns. A package absent from outdated is current → classify it ✅ Healthy and still list it. Never silently drop a direct dependency from the report. Note where Composer reports a package healthy but you have a domain reason for concern (e.g. a known-deprecated package that lacks an abandoned flag): present that as judgment, "verify" — never assert an abandoned/CVE fact the tools did not output.
4. Resolve the safest fix version for each vulnerable package (least-disruptive remediation)
Before recommending anything for a 🔴 vulnerable package, work out the smallest change that clears it. Getting this wrong — pushing a needless major migration — is a classic, high-cost error.
- Read each
affectedVersionsrange fromcomposer auditliterally. A range
like >=2.0.0, --all and confirm the target version is in the published list before you recommend it (and before you claim a branch is a dead end).
- Recommend the latest stable that the project can satisfy, but also state the
security floor: e.g. "≥7.4.5 clears the CVEs; 7.10.6 is current stable and semver-safe". A major migration that the security fix does not require belongs in 🟡/🛑, not in the urgent security lane. State the floor as "fixed in ≥X" / "clears at X" — never restate the affected ` is a no-op**: it cannot move past the pin. The remediation is to **change the constraint**, so advise composer require ":" (the human runs it; see step 8). **Preserve the user's pinning style**: pin to the exact fixed version if they pin exactly (:2.0.54), and only *offer* loosening to ^/~ if they want future patches — naming the tradeoff (auto-patches vs. a deliberate, audited pin). Treat the existing pin as intentional, not a defect. **Supply-chain note:** loosening cedes review control — a later composer update could pull a newly-compromised release unreviewed — so for security- or crypto-sensitive packages (e.g. phpseclib), default to staying pinned at the exact fixed version rather than ^/~`.
> ⚠️ Misreading ` major when a drop-in patch would have closed the CVE is the single most damaging > mistake this skill can make. Compute the floor, confirm it exists, prefer the > patch.
5. Recommend replacements for abandoned packages (headline)
For each 🟠 Abandoned package — this is the skill's core value-add:
- If Packagist's
abandonedflag names a replacement, lead with it. - If it says "No replacement was suggested", consult
[references/abandoned-replacements.md](references/abandoned-replacements.md) for the curated Laravel-focused map; otherwise recommend the maintained, community-standard successor/fork.
- State the migration shape: drop-in vs. namespace/API change.
- Mark every replacement "verify before adopting" — it is judgment, not a
fact from Composer (see [Confidence](#confidence--honesty)).
6. Compatibility check before recommending any major
Before you recommend a major bump, confirm it actually fits the project:
- Check the candidate version's
require(PHP and Laravel/illuminate/*
constraints) against the project's current PHP and Laravel versions from step 1.
- If the latest needs a newer PHP or Laravel than the project has, do not
recommend the bump. Mark it 🔒 blocked-on-upgrade and hand it to the upgrade path. See [references/laravel-compatibility.md](references/laravel-compatibility.md).
- Flag first-party packages whose major tracks the framework — those belong
in a Laravel upgrade, not a casual bump.
7. Risk + effort per recommended package — will it break the app?
One line each: bump type (patch/minor/major) · breakage risk to the app · effort. The user's running app must not break, so make breakage risk explicit:
- Blast radius. Does the bump drag transitive dependencies with it? Advise
previewing with composer update --with-dependencies --dry-run and reading what else moves. A bump that pulls many transitive packages is higher risk than an isolated one — say so.
- Resolver feasibility. That same dry-run proves Composer can satisfy the whole
constraint set; a conflict there means don't run the real update yet — report the conflict instead of a green light.
- API/namespace surface. Drop-in (patch/minor, no API change) vs. code changes
likely (major, renamed namespaces/signatures). Point majors at the package's UPGRADE/CHANGELOG.
- Effort label: "drop-in", "skim changelog", "read upgrade guide", "code
changes likely".
8. Prioritized plan → three lanes
Order by value and safety, then emit exact commands grouped into lanes:
- Security fixed by a verified in-major drop-in (from step 4) → ✅ Do now
- Security that requires a major migration → ⚠️ Do carefully (or 🛑 if blocked)
- Abandoned (with replacement) → ⚠️ Do carefully
- Safe minors/patches → ✅ Do now
- Risky majors / framework-coupled / blocked-on-upgrade → 🛑 Defer
- Dev dependencies → grouped separately, lower urgency unless vulnerable
- ✅ Do now — security drop-ins + safe bumps; exact commands —
composer update , or composer require ":" for an exact-pinned dependency (step 4).
- ⚠️ Do carefully — abandoned swaps and majors needing changelog/code work;
command plus the homework.
- 🛑 Defer — risky/blocked majors; say what must happen first.
Always advise — so the app can't silently break:
- work on a branch, never on main;
- run
composer update --with-dependencies --dry-runfirst — confirm the
resolver succeeds and review every package that moves before the real run;
- for an exact-pinned package (step 4), the fix is
composer require ":" — this rewrites the constraint and updates in one step; plain composer update won't budge a pin. Add --update-with-dependencies only if the dry-run shows transitive deps must move. Put the constraint change first in the sequence, and preview it with composer require ":" --dry-run before the real run;
- apply one package / one lane at a time, committing
composer.lockbetween
steps so any breakage is isolated and easy to revert;
- run the full test suite (plus a quick smoke check of the app) after each step;
- prefer a verified drop-in security patch over a major migration for the urgent
fix, and defer the major (per step 4).
The user runs every command — you don't.
9. Output — a clean Markdown report
Group by priority using [examples/report.md](examples/report.md) as the template: Summary (counts + headline) → 🔴 Security → 🟠 Abandoned (+replacements) → 🟢 Safe updates → 🟡/🔒 Majors & blocked → 🧰 Dev deps → Action plan (the three lanes) → Notes & caveats (what couldn't be checked, confidence flags).
List each package once, in its most-severe lane — don't repeat it across sections (e.g. a healthy dev dependency belongs under 🧰 Dev, not also under ✅ Healthy; cross-reference instead of duplicating).
10. Saving the report (optional, on request only)
By default, output the report to the conversation only — write nothing to disk.
After presenting the report, you may offer to save it, proposing this default path:
storage/logs/dependency-triage-.md
Then write the file only if the user agrees (or asked to save in the first place). Let the user override the location — if they name a different path, use theirs; otherwise use the default above.
storage/logs/is Laravel-native and is already git-ignored by default, so the
report won't accidentally land in the repo (the user can commit it if they want).
- If
storage/logs/doesn't exist (non-Laravel project), **ask the user where to
save** instead of guessing a path.
- Date the filename so repeated runs don't overwrite each other.
- Never save silently or without confirmation — offering + waiting for a yes is fine;
writing unprompted is not.
This is the only file the skill may ever write, and only when asked. It writes a new report artifact — it still never touches composer.json, the lockfile, or any source code. See [Guardrails](#guardrails).
Confidence & honesty
Separate the two clearly in every report:
- Hard facts — installed/latest versions, CVE IDs, abandoned flags. These come
straight from composer outdated / composer audit. Cite the tool.
- Judgment — replacement suggestions, effort estimates, risk calls. These are
AI-derived advice. Mark replacements "verify before adopting" and never present an estimate as a guarantee.
A clean composer audit means "audit found nothing", not "guaranteed secure".
Robustness & fallbacks
State plainly what you could not check rather than guessing:
- **Old Composer ( --all` and check
the published version list. An advisory --all before recommending anything.
- ❌ Push a major migration when a verified drop-in patch closes the CVE —
escalate to a new major only when the current major has no fixed release.
- ❌ Drop a direct dependency from the report because it wasn't in
composer outdated — it's current; list it as ✅ Healthy.
- ❌ Green-light an update without a
--dry-run— preview the resolved set so a
bump can't silently break the app or fail to resolve.
- ❌ Recommend
composer updatefor an exact-pinned dependency — it's a
no-op that can't pass the pin. Advise composer require ":" to change the constraint instead (step 4/8), and don't bury that as an afterthought.
- ❌ Summarize advisories from a truncated view — read the full audit output,
count advisories per package, and cross-check the total. A silently
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ArtemProshkovskiy
- Source: ArtemProshkovskiy/laravel-maintenance-skills
- License: MIT
- Homepage: https://ArtemProshkovskiy.github.io/laravel-maintenance-skills/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.