Skip to content
Back to skills

Map Dependencies

ASecurity

Cite which project depends on which from build manifests, with the file and declaration on every edge. Use when: 'map dependencies', 'project reference graph', 'dependency graph', 'what references what', 'internal dependencies', 'package references', 'which projects depend on which'. Skip when: the question is which repositories exist, which is /architecture:map-landscape, shallow modules, which is /architecture:improve, or source imports and call graphs.

  • 20 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 29, 2026
ai-agentspythonrustgorubyphpshellbashsqlnodeaws

Works with

  • cli

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add melodic-software/claude-code-plugins --skill map-dependencies --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Map Dependencies?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Map Dependencies
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/melodic-software-map-dependencies/badge)](https://www.skillsdirectory.com/skills/melodic-software-map-dependencies)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
description: "Cite which project depends on which from build manifests, with the file and declaration on every edge. Use when: 'map dependencies', 'project reference graph', 'dependency graph', 'what references what', 'internal dependencies', 'package references', 'which projects depend on which'. Skip when: the question is which repositories exist, which is /architecture:map-landscape, shallow modules, which is /architecture:improve, or source imports and call graphs."
argument-hint: "[path] [--include-external] [--external-only] [--cycles-only] [--out <dir>]"
user-invocable: true
disable-model-invocation: false
shell: bash
metadata:
  workflow-stage: explore
  summary: Cite project and package edges from build manifests
---

## Repository context

The current repository is the default subject. Collect its root with one Bash call,
`git rev-parse --show-toplevel`. Treat a failure as an unknown root and ask for a path.
Do not walk the working directory for nested repositories.

## Purpose

Answer "which project depends on which, and in which direction" from build declarations
a script read. Every edge names the file and the declaration it came from. The canonical
artifact is `dependency-graph.json`. The human render is a mermaid `flowchart` of the
internal edges. This is the model, not a C4 diagram.

The dialect decision lives in `${CLAUDE_PLUGIN_ROOT}/reference/config.md`. Read it.
Do not restate it, and do not emit a C4 view from this skill. This flowchart does
not follow `landscape_dialect`. `diagram_dialect.system` stays the planning opt-in.

## Resolve home

Read `${CLAUDE_PLUGIN_ROOT}/reference/config.md` for `architecture_dir`. Run
`bash "${CLAUDE_PLUGIN_ROOT}/lib/resolve-convention-home.sh" --root "${CLAUDE_PROJECT_DIR}"`
and follow the exit code. Never parse the root instruction file yourself.

`--out <dir>` wins for this run alone. Then a declared `architecture_dir`. With neither,
including every non-interactive run, stop and point at `/architecture:setup`. This skill
never writes the topic doc.

## Build the graph

```bash
"${CLAUDE_SKILL_DIR}/scripts/dependency-graph.sh" \
  --out "<architecture_dir>/dependency-graph.json" "<repo-path>"
```

The script writes the record itself and exits 1 when it cannot. `generated_on` is the
HEAD commit date (`unknown` with no commit), so a second run on the same commit is
byte-identical; `--generated-on <date>` overrides it. The document is one object per
line. Do not pretty-print it. A reader given another layout exits 1.

`result` is `ok` or `unknown`. `unknown` means no shipped adapter could read the tree.
The message says which manifests were found. That is the answer. Do not draw a diagram,
and do not fill the arrays by hand. An empty graph is `result` `ok` with project nodes
and no edges. Read it as a repository that declares no references only when no
`unread-reference-tags` or `unread-manifest` finding exists. The first means the collector
skipped reference tags in a file, and the edge list is short by that count. The second
means a manifest holds a declaration whose shape no reader handles; its evidence is the
file, a colon, and the declaration skipped, one finding per declaration.

Every shipped adapter whose manifests are present runs, and their nodes, edges and
findings are one record. `ecosystem` is the adapter's name when one ran, `mixed` when
more than one ran, and `unknown` when none did. Each node carries its own `ecosystem`.
The header of `scripts/dependency-graph.sh` records the upstream source, verification date
and recheck trigger behind each non-.NET adapter's rules below.

The .NET adapter:

- `ProjectReference` is a directed internal edge. The target is the `Include` path
  relative to the project file. A target that is missing, or that resolves outside the
  repository root, is `status` `unresolved`. Never match it to a project of the same
  name somewhere else on disk.
- `PackageReference` is an external package edge. The node id is `pkg:` plus the Include.
- A `ProjectReference` or `PackageReference` in a `Directory.Build.props` or
  `Directory.Build.targets` is an edge from every project under that file's folder whose
  nearest such file it is, and the evidence cites the props file. A project never gets an
  edge to itself that way. Any other `.props` or `.targets` file, and
  `Directory.Packages.props`, has no known importer, so its references are counted in an
  `unread-reference-tags` finding and not drawn.
- `*.sln` and `*.slnx` contribute membership. A project path that does not resolve inside
  the root is a finding, not an edge.
- Source files are not read. A `using` or an import is not an edge.

The Node adapter:

- Every `package.json` outside `node_modules` is a project node, id its repo-relative path.
  Workspace members are the package folders that the `workspaces` globs (array or
  `{"packages": [...]}` form) and a `pnpm-workspace.yaml` `packages` list expand to.
- A dependency, dev, peer or optional dependency that names a member of the declaring
  package's workspace, or whose spec is `workspace:`, `file:` or `link:` pointing at a
  package folder inside the root, is an internal project edge. Every other entry is an
  external package edge to `pkg:node:<name>`.
- A spec that names no member, leaves the root, or points at a folder with no `package.json`
  is `unresolved`. Never match it to a package of the same name elsewhere on disk.
- A negated glob, a flow-list `packages:`, a `catalog:` spec and a non-string dependency
  value are `unread-manifest` findings, not edges.

The Go adapter:

- Every `go.mod` is a project node, id its repo-relative path, name its module path.
- A `replace` whose target is a local path (`./` or `../`) holding a `go.mod` inside the
  root is an internal project edge, and the evidence cites the `replace` line. A missing
  or out-of-root target is `unresolved`.
- A `require` of a module that is another `go.mod` in the repo is internal only when a
  `replace` or a `go.work` `use` line points it there; otherwise it is an external edge
  to `pkg:go:<module>`. `go.work` `use` lines are membership.
- Any other directive the reader cannot parse, a `go.work` `replace`, and a `use` line
  naming no `go.mod` in the root are `unread-manifest` findings.

The Python adapter:

- Every `pyproject.toml` is a project node, id its repo-relative path. A `setup.py` or
  `requirements*.txt` with no manifest beside it is a node too; one beside a manifest is
  read as that project.
- A path reference is an internal project edge when it names a folder inside the root
  holding a `pyproject.toml` (else a `setup.py`), and the evidence cites the declaration.
  Path references are a `name @ file:` dependency string, a poetry or uv `path =` entry,
  and a `-e`, `./` or `../` requirements line. A missing or out-of-root path is
  `unresolved`.
- A `[tool.uv.workspace]` `members` glob, minus `exclude`, is an internal project edge from
  the workspace root to each `pyproject.toml` it matches; a literal member holding none is
  `unresolved`.
- Every other named requirement is an external edge to `pkg:python:<normalized name>`.
  `-r` includes are followed only inside the root.
- `setup.py` is never executed or parsed; each one is an `unread-manifest` finding. So
  are dynamic dependencies, a uv `workspace = true` source, a multi-line inline table, and
  an include that is missing or outside the root.

The Rust adapter:

- Every `Cargo.toml` is a project node, id its repo-relative path.
- A `path =` dependency in `[dependencies]`, `[dev-dependencies]` or `[build-dependencies]`
  is an internal project edge when it names a folder inside the root holding a
  `Cargo.toml`, and the evidence cites the declaration. A missing or out-of-root path is
  `unresolved`.
- A `[workspace]` `members` glob, minus `exclude`, is an internal project edge from the
  workspace root to each `Cargo.toml` it matches; a literal member holding none is
  `unresolved`.
- A dependency written `workspace = true` takes its source from the nearest ancestor
  `[workspace.dependencies]`: a `path =` entry is an internal edge, any other entry an
  external one, and the evidence cites both declarations. A workspace entry no member
  inherits draws no edge.
- Every other dependency is an external edge to `pkg:rust:<name>`.
- Any `[target.*]` dependency table, a `path =` under `[patch]` or `[replace]`, a
  `workspace = true` with no workspace entry to resolve it, a multi-line inline table, and
  a members glob the reader cannot resolve are `unread-manifest` findings.

The JVM adapter (Gradle and Maven):

- Every `pom.xml`, `build.gradle` and `build.gradle.kts` is a project node, id its
  repo-relative path. A folder with a `settings.gradle(.kts)` and no build file is a node
  under the settings file.
- A `settings.gradle(.kts)` `include` argument is an internal project edge from the settings
  folder's project to the folder its project path names, when that folder holds a build
  file. A `project(':x')` or `project(path = ':x')` dependency in a build file resolves from
  the nearest settings file above it. A `pom.xml` `<modules><module>` entry is an internal
  project edge to the `pom.xml` in the folder it names. Each edge cites the declaration.
  A project path or module naming no build file, or leaving the root, is `unresolved`.
- No external package edge is drawn for JVM.
- `includeFlat`, `includeBuild`, an include holding a variable, an interpolated string or a
  spread, anything inside a loop, a `projectDir`, `buildFileName` or `name` assignment,
  `apply from`, a `projects.x` type-safe accessor, a `project(...)` with a non-literal
  argument, a module holding a `${property}`, and a module naming a pom file not called
  `pom.xml` are `unread-manifest` findings.

Read: .NET, Node, Go, Python, Rust, and JVM (Gradle and Maven). Declined: Ruby (`Gemfile`) and
PHP (`composer.json`) are detected and named in the record's `message`, never parsed. The
unread rule: a manifest or declaration no reader handles is reported, never guessed at and
never dropped. A tree holding only declined manifests is `unknown`; in a tree a reader ran on,
the message still names the declined ecosystems and each skipped declaration is an
`unread-manifest` finding.

`node_threshold` in the record (40) is the documented count of internal project nodes
above which the human diagram aggregates to directories. The JSON stays at project
resolution.

## Render

```bash
"${CLAUDE_SKILL_DIR}/scripts/render-dependencies.sh" \
  --record "<architecture_dir>/dependency-graph.json" \
  --out "<architecture_dir>"
```

This writes `dependency-graph.md`. The summary line on stdout is the report's counts.
Keep it. Do not redraw the flowchart yourself.

- Default: internal project edges only. External packages are counted and collapsed.
- `--include-external`: draw package edges as well.
- `--external-only`: draw package edges and not internal project edges.
- `--cycles-only`: draw only internal edges that sit on a reported cycle.
- `--include-external` and `--external-only` together are a usage error.

Cycles are a section at the top of `dependency-graph.md`. Above the threshold the diagram
says it aggregated to directory level.

## Close with the report

- **Artifacts**: `dependency-graph.json` and `dependency-graph.md`, or `none written` when
  there was no `architecture_dir` and no `--out`.
- **Result**: `ok` or `unknown`, and the message when it is non-empty.
- **Counts**: quote the summary line. Do not count nodes by hand.
- **Unresolved**: how many project references and solution memberships did not resolve,
  and that none of them were matched by file name.
- **Unread**: the `unread_files` count, each file and tag count from the
  `unread-reference-tags` findings, and each skipped declaration from the
  `unread-manifest` findings.
- **Cycles**: the cycle lines, or none. Each line is one witness cycle for a group of
  projects that depend on each other, not every cycle in the group.
- **Aggregation**: `no`, or `yes` with the threshold the artifact states.
- **Diagram**: mermaid flowchart, or no diagram because the result is unknown or a filter
  left nothing to draw.

## Interactive view

After the report, offer an interactive view of `dependency-graph.json` in one sentence. The markdown and the
record stay authoritative. Build it only with `${CLAUDE_PLUGIN_ROOT}/scripts/build-view.mjs dependencies`,
never hand-written; the publish destination comes from the `medium` cascade key. Procedure:
[`${CLAUDE_PLUGIN_ROOT}/reference/rendered-view.md`](${CLAUDE_PLUGIN_ROOT}/reference/rendered-view.md).

## What this skill does NOT do

- Import graphs, call graphs, or runtime discovery.
- A C4 component, container, context, or deployment view. The component view that reads
  this graph is the successor below.
- Resolve a `ProjectReference` by searching the disk for a matching file name.
- Invent an empty graph for an ecosystem this skill does not read.
- Fetch, or modify any file in the subject repository other than the two artifacts in
  the architecture directory.
- Choose a checkout identity. Project ids are repo-relative paths. They are not the
  directory name of the clone.

## Next

/architecture:map-components

The component view reads dependency-graph.json.

## Gotchas

- **The shared .NET reader is the one portfolio-facts uses.** It reads each reference tag
  as a whole, so an `Include` on a later line and a single-quoted `Include` are edges. A
  reference inside an XML comment is not, and an `Update` or `Remove` override is not a
  reference. A reference-like tag it cannot turn into an edge (`FrameworkReference`,
  `GlobalPackageReference`, an empty or missing `Include`) is counted in the
  `unread-reference-tags` finding.
- **Directory.Build.props edges follow MSBuild's lookup.** MSBuild imports the nearest
  `Directory.Build.props` above a project and stops there, and a relative `Include` in an
  imported file is relative to the importing project's folder. This collector does not
  evaluate `Condition` or `$(...)` properties, so a props reference that a condition
  would exclude is still drawn, and one built from a property is `unresolved`.
  - Claim: MSBuild walks up from the project to the first `Directory.Build.props` and
    imports that one; an imported file's relative `Include` resolves from the project's
    folder.
  - Basis: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory
    and https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-items
  - As of: 2026-09-29.
  - Recheck: when either page changes the lookup rule or the base of a relative `Include`.
- **Evidence stops at the end of the tag.** A `Version` child element on the following
  lines is not part of the citation. A `Version` attribute on the same tag is.
- **`bin`, `obj`, `node_modules`, `vendor`, and dot-directories** other than the CI and
  devcontainer directories are not walked. A project that lives only there is absent.
- **A solution folder is not a project.** Only paths ending in `.csproj` or `.fsproj`
  are membership. A member with another project extension (`.vbproj`, `.sqlproj`) is an
  `unread-manifest` finding.
- **Aggregation is a view.** `dependency-graph.json` stays one node per project. The
  markdown says when the flowchart collapsed to directories.
- **The flowchart does not follow `landscape_dialect`.** That key is the system landscape.
  This diagram is a mermaid flowchart either way.

Files in this skill

  • SKILL.md6.5 KB
  • evals/evals.json4 KB
  • scripts/dependency-graph.sh18.7 KB
  • scripts/dependency-graph.test.sh21 KB
  • scripts/render-dependencies.sh13.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…