From oss-health-skills
Extract dependencies from projects/SBOMs and map them to GitHub repositories
How this skill is triggered — by the user, by Claude, or both
Slash command
/oss-health-skills:dependency-extractorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Extract dependencies from a project using SBOM (Software Bill of Materials) tools and map them to GitHub repositories.
Extract dependencies from a project using SBOM (Software Bill of Materials) tools and map them to GitHub repositories.
This skill requires the GitHub CLI (gh) to be installed and authenticated.
Run this first to verify your setup:
gh --version && gh auth status
If gh is not installed, you'll see "command not found". Install it from: https://cli.github.com
If gh is not authenticated, run:
gh auth login
Without gh, this skill has severely limited functionality:
Check the platform first before running commands. Many bash commands fail on Windows.
# Returns: win32, darwin, or linux
node -e "console.log(process.platform)"
On Windows, avoid these patterns that will fail:
base64 -d - doesn't existgrep -oP - Perl regex not available\\. - escaping differswhile read loops - breaksUse raw GitHub URLs instead of base64 decoding:
https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}
This works everywhere and avoids all encoding issues. Use WebFetch or curl to retrieve content directly.
/dependency-extractor <path-or-sbom>
Where <path-or-sbom> is either:
owner/repo)Large SBOM files: SBOM files can exceed 100k lines. Do NOT use the Read tool directly - use Node.js to parse JSON files. See "Parsing SBOM Files" section below.
Use gh api with tree endpoint to find all dependency files at once.
Cross-platform (simple filters):
# Get all paths, then filter for specific extensions
gh api repos/{owner}/{repo}/git/trees/HEAD?recursive=1 --jq "[.tree[].path | select(endswith(\".csproj\") or endswith(\"package.json\") or endswith(\".props\") or endswith(\"requirements.txt\") or endswith(\"go.mod\") or endswith(\"Cargo.toml\"))] | .[]"
Unix/macOS only (regex filters):
gh api repos/{owner}/{repo}/git/trees/HEAD?recursive=1 --jq '
.tree[].path | select(
test("(package\\.json|package-lock\\.json|yarn\\.lock|pnpm-lock\\.yaml)$") or
test("(Directory\\.Packages\\.props|Directory\\.Build\\.props|\\.csproj|packages\\.config)$") or
test("(requirements.*\\.txt|pyproject\\.toml|Pipfile|setup\\.py)$") or
test("(go\\.mod|go\\.sum)$") or
test("(Cargo\\.toml|Cargo\\.lock)$") or
test("(Gemfile|Gemfile\\.lock)$") or
test("(pom\\.xml|build\\.gradle(\\.kts)?)$") or
test("(composer\\.json|composer\\.lock)$")
)
'
Recommended: Use raw GitHub URLs (works on all platforms):
https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}
Example: Fetch and analyze with WebFetch or curl:
curl -s "https://raw.githubusercontent.com/owner/repo/main/package.json"
Unix/macOS only (base64 decoding):
# For text files (package.json, requirements.txt, etc.)
gh api repos/{owner}/{repo}/contents/{path} --jq '.content' | base64 -d
# For .csproj files - extract PackageReference items
gh api repos/{owner}/{repo}/contents/{path} --jq '.content' | base64 -d | \
grep -oP '(?<=Include=")[^"]+'
# For package.json - extract dependencies
gh api repos/{owner}/{repo}/contents/package.json --jq '.content' | base64 -d | \
jq -r '.dependencies // {} | keys[]'
Windows: The base64 -d and grep -oP commands above will fail. Use raw GitHub URLs instead.
Cross-platform (use raw URL):
curl -s "https://raw.githubusercontent.com/{owner}/{repo}/main/Directory.Packages.props"
# Then parse the XML to extract PackageReference Include="" values
Unix/macOS only:
gh api repos/{owner}/{repo}/contents/Directory.Packages.props --jq '.content' | base64 -d | \
grep -oP '(?<=Include=")[^"]+'
# Get all package.json files in repo
gh api repos/{owner}/{repo}/git/trees/HEAD?recursive=1 --jq '
.tree[] | select(.path | endswith("package.json")) | .path
' | while read path; do
echo "=== $path ==="
gh api "repos/{owner}/{repo}/contents/$path" --jq '.content' | base64 -d | jq '.dependencies'
done
IMPORTANT: Always scan for ALL dependency files in the repository. Modern projects often use multiple ecosystems:
| Ecosystem | Dependency Files to Find |
|---|---|
| .NET/NuGet | Directory.Packages.props, *.csproj, packages.config, paket.dependencies |
| Node.js/npm | package.json, package-lock.json, yarn.lock, pnpm-lock.yaml |
| Python | requirements.txt, pyproject.toml, Pipfile, setup.py |
| Go | go.mod, go.sum |
| Rust | Cargo.toml, Cargo.lock |
| Ruby | Gemfile, Gemfile.lock |
| Java | pom.xml, build.gradle, build.gradle.kts |
| PHP | composer.json, composer.lock |
Search the entire repository tree for these files. A full-stack application will typically have:
src/, client/, web/, ui/ folders)If given a project directory without an existing SBOM, suggest generating one:
sbomify scan <project-path> -o sbom.json
syft <project-path> -o cyclonedx-json > sbom.json
# Node.js
cyclonedx-npm --output-file sbom.json
# Python
cyclonedx-py requirements -o sbom.json
# Go
cyclonedx-gomod mod -output sbom.json
SBOM files can be very large (100k+ lines). Do NOT try to read them directly with the Read tool - it will fail with token limit errors.
Use the helper script (recommended):
node ~/.claude/skills/dependency-extractor/scripts/extract-sbom-repos.js <sbom-file> --filter-corporate
Options:
--filter-corporate - Exclude repos owned by Google, Microsoft, Docker, etc.--json - Output as JSON for further processingOr use inline Node.js to parse large JSON files:
// Save as extract-sbom.js and run with: node extract-sbom.js <path-to-sbom>
const fs = require('fs');
const path = process.argv[2];
const sbom = JSON.parse(fs.readFileSync(path));
// CycloneDX format
if (sbom.components) {
console.log('Format: CycloneDX');
console.log('Total components:', sbom.components.length);
// Extract GitHub repos from package names or purls
const repos = [...new Set(
sbom.components
.map(c => {
// Try to extract from name (Go modules)
const nameMatch = c.name?.match(/github\.com\/([^\/]+\/[^\/]+)/);
if (nameMatch) return nameMatch[1];
// Try to extract from purl
const purlMatch = c.purl?.match(/github\.com[%2F\/]([^\/]+)[%2F\/]([^@\/]+)/i);
if (purlMatch) return `${purlMatch[1]}/${purlMatch[2]}`;
// Try externalReferences
const extRef = c.externalReferences?.find(r =>
r.url?.includes('github.com') && (r.type === 'vcs' || r.type === 'website')
);
if (extRef) {
const match = extRef.url.match(/github\.com\/([^\/]+\/[^\/]+)/);
if (match) return match[1].replace(/\.git$/, '');
}
return null;
})
.filter(Boolean)
)];
console.log('GitHub repos found:', repos.length);
repos.forEach(r => console.log(r));
}
// SPDX format
if (sbom.packages) {
console.log('Format: SPDX');
console.log('Total packages:', sbom.packages.length);
// Similar extraction logic...
}
Quick one-liner for component count:
node -e "console.log(JSON.parse(require('fs').readFileSync('sbom.json')).components.length)"
Extract first N components for preview:
node -e "JSON.parse(require('fs').readFileSync('sbom.json')).components.slice(0,20).forEach(c => console.log(c.name, c.version))"
const corporate = [
'google', 'golang', 'microsoft', 'docker', 'containerd', 'kubernetes',
'azure', 'aws', 'amazon', 'hashicorp', 'cloudflare', 'facebook', 'meta',
'apple', 'ibm', 'oracle', 'vmware', 'redhat', 'grpc', 'prometheus',
'opencontainers', 'moby'
];
const nonCorporate = repos.filter(r =>
!corporate.some(c => r.toLowerCase().startsWith(c + '/'))
);
{
"components": [
{
"name": "lodash",
"version": "4.17.21",
"purl": "pkg:npm/[email protected]",
"externalReferences": [
{
"type": "vcs",
"url": "https://github.com/lodash/lodash.git"
}
]
}
]
}
Extract GitHub repos from:
externalReferences where type is vcs or websitepurl for package identifier to query registriesname field directly (e.g., github.com/spf13/cobra){
"packages": [
{
"name": "lodash",
"versionInfo": "4.17.21",
"externalRefs": [
{
"referenceType": "purl",
"referenceLocator": "pkg:npm/[email protected]"
}
],
"homepage": "https://github.com/lodash/lodash"
}
]
}
When SBOM lacks GitHub URLs, query package registries:
curl -s "https://api.nuget.org/v3/registration5-gz-semver2/{package}/index.json" | \
gunzip | jq -r '.items[-1].items[-1].catalogEntry.projectUrl'
Or scrape from nuget.org:
curl -s "https://www.nuget.org/packages/{package}" | \
grep -oP '(?<=href=")[^"]*github\.com/[^"]*(?=")'
curl -s "https://registry.npmjs.org/{package}" | jq -r '.repository.url'
Batch lookup (multiple packages):
echo "lodash express chalk" | tr ' ' '\n' | while read pkg; do
url=$(curl -s "https://registry.npmjs.org/$pkg" | jq -r '.repository.url // empty')
echo "$pkg: $url"
done
curl -s "https://pypi.org/pypi/{package}/json" | \
jq -r '.info.project_urls.Source // .info.project_urls.Repository // .info.project_urls.Homepage // .info.home_page'
curl -s "https://crates.io/api/v1/crates/{package}" | jq -r '.crate.repository'
# Go modules often use GitHub paths directly
# e.g., github.com/gin-gonic/gin -> gin-gonic/gin
# Just extract the path after github.com/
# For indirect deps, check pkg.go.dev:
curl -s "https://pkg.go.dev/{module}" | grep -oP 'github\.com/[^"<]+'
curl -s "https://rubygems.org/api/v1/gems/{package}.json" | \
jq -r '.source_code_uri // .homepage_uri'
# Use search.maven.org API
curl -s "https://search.maven.org/solrsearch/select?q=a:{artifactId}+AND+g:{groupId}&rows=1&wt=json" | \
jq -r '.response.docs[0].scm // empty'
Convert various Git URL formats to owner/repo:
| Input | Output |
|---|---|
https://github.com/lodash/lodash.git | lodash/lodash |
git+https://github.com/lodash/lodash.git | lodash/lodash |
git://github.com/lodash/lodash.git | lodash/lodash |
[email protected]:lodash/lodash.git | lodash/lodash |
github.com/lodash/lodash | lodash/lodash |
Discard non-GitHub URLs (GitLab, Bitbucket, etc.) with a note in output.
lodash/lodash, expressjs/express, mochajs/mocha, chalk/chalk
## Extracted Dependencies
**Total packages:** 47
**With GitHub repos:** 42
**Non-GitHub/Unknown:** 5
### GitHub Repositories
| Package | Ecosystem | GitHub Repo |
|---------|-----------|-------------|
| lodash | npm | lodash/lodash |
| express | npm | expressjs/express |
| requests | pypi | psf/requests |
### Non-GitHub Sources
| Package | Ecosystem | Source |
|---------|-----------|--------|
| internal-lib | npm | https://gitlab.company.com/... |
### No Source Found
- some-abandoned-package (npm)
- legacy-util (pypi)
### Ready for Funding Analysis
Copy this for `/dependency-funding-analysis`:
\`\`\`
lodash/lodash, expressjs/express, psf/requests, ...
\`\`\`
When running, consider filtering:
The fastest way to get package to GitHub repo mapping with extra metadata:
# Get package info including repo URL
curl -s "https://api.deps.dev/v3/systems/{ecosystem}/packages/{package}" | \
jq '{name: .package.name, repo: .package.projectUrl, versions: .versions | length}'
# Ecosystems: npm, go, maven, pypi, cargo, nuget
Example:
curl -s "https://api.deps.dev/v3/systems/npm/packages/express" | \
jq -r '.package.projectUrl'
# Returns: https://github.com/expressjs/express
Batch lookup with deps.dev:
for pkg in lodash express chalk; do
repo=$(curl -s "https://api.deps.dev/v3/systems/npm/packages/$pkg" | jq -r '.package.projectUrl // empty')
echo "$pkg -> $repo"
done
raw.githubusercontent.com) instead of base64 decoding. Use simple endswith() jq filters instead of regex test() patterns.node -e or a script file to parse JSON.$variable in PowerShell commands from bash - the $ gets eaten. Write a .js file instead.npx claudepluginhub lottepitcher/oss-health-skills --plugin oss-health-skillsScans project dependencies across ecosystems for vulnerabilities, generates SBOMs, assesses risks, and provides automated remediation strategies. Useful for auditing packages, license compliance, and supply chain security.
Evaluates packages, manages dependencies, and addresses supply chain security for npm/pip/cargo/bundler/Go. Use for auditing packages, reviewing lockfiles, checking vulnerabilities, comparing alternatives, assessing trustworthiness.
Audits project dependencies for bloat, unused packages, security risks, supply-chain issues, and upgrades by building import graphs and verifying call sites in npm, pip, Cargo, Go, Ruby projects.