Skip to content
Back to skills

Understand Explain

ASecurity

Explains one file, function, class, table or folder of a codebase in depth, starting from the knowledge graph built by the Understand-Anything plugin and confirming every statement in the source. Use when someone says "explain this file", "how does this function work", "walk me through the scheduler module", "what is this class for and who uses it", "run /understand-explain src/services/scheduler.ts", "I inherited this code, what does it do", or needs a written deep dive on a specific part of...

  • 142 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 6, 2026
developmenttypescriptrustgoshellbashsqlnodegitapidatabase

Works with

  • claude code
  • terminal
  • cli
  • api

Security analysis

A100/100

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

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill understand-explain --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Understand Explain?

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

Security grade badge for Understand Explain
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-understand-explain/badge)](https://www.skillsdirectory.com/skills/terminalskills-understand-explain)

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
---
name: understand-explain
description: >-
  Explains one file, function, class, table or folder of a codebase in depth,
  starting from the knowledge graph built by the Understand-Anything plugin and
  confirming every statement in the source. Use when someone says "explain this
  file", "how does this function work", "walk me through the scheduler module",
  "what is this class for and who uses it", "run /understand-explain
  src/services/scheduler.ts", "I inherited this code, what does it do", or needs
  a written deep dive on a specific part of a project.
license: Apache-2.0
compatibility: "A repository analysed with Understand-Anything 2.9+ (a knowledge-graph.json in .ua/ or .understand-anything/); jq 1.6+, git and standard shell tools (sed, nl, grep)"
metadata:
  author: terminal-skills
  version: "2.0.0"
  category: development
  tags: ["code-explanation", "knowledge-graph", "codebase-analysis", "legacy-code", "understand-anything"]
  repository: https://github.com/Egonex-AI/Understand-Anything
---

# /understand-explain: Deep Dive on a File or Function with Understand-Anything

## Overview

Understand-Anything is an open-source plugin for coding agents. `/understand` analyses a repository once and saves a knowledge graph as JSON; `/understand-explain` takes one target, such as `src/services/scheduler.ts` or `src/services/scheduler.ts:bookSlot`, and explains it.

Reading a file tells what its lines do. It does not tell why the file exists, which requests end up in it, what it is allowed to assume about its inputs, or what would break if it changed. The graph holds exactly that surrounding picture: the node's summary and tags, its architectural layer, the tour step that introduces it, and every relationship in and out. This skill pulls that picture in one query, then reads the code and writes an explanation in which the context comes from the graph and every statement about behaviour comes from a line that was opened.

Graph fields it reads:

| Field | Role in the explanation |
|---|---|
| `nodes[]`: `id`, `type`, `name`, `filePath`, `lineRange`, `summary`, `tags`, `complexity`, `languageNotes` | finding the target and its exact lines; a first hypothesis of its purpose |
| `edges[]`: `source`, `target`, `type` | what the target relies on and what uses it |
| `layers[]`: `name`, `description`, `nodeIds` | where it sits in the architecture |
| `tour[]`: `order`, `title`, `nodeIds`, `languageLesson` | whether the analysis considered it part of the main story |
| `project.gitCommitHash` | whether the graph still matches the file |

No graph in the project: install the plugin (in Claude Code, `/plugin marketplace add Egonex-AI/Understand-Anything` and `/plugin install understand-anything`) and run `/understand`. Installation on other hosts and the analysis options are covered in the `understand-chat` skill.

## Instructions

### 1. Settle the target and the reader

The target comes from the request. Accept any of these forms:

| Given | Meaning |
|---|---|
| `src/lib/time.ts` | a file |
| `src/lib/time.ts:overlaps` | a function or class inside a file |
| `overlaps` or `SlotTakenError` | a bare name, to be looked up |
| `src/db/` | everything under a folder |
| "the booking logic", "whatever sends reminders" | a description; turn it into names first by matching `tags` and `summary` |

Decide who is reading, because it changes the depth and the vocabulary. If the request does not say, assume the second row.

| Reader | Write |
|---|---|
| Knows programming, not this language | the standard explanation plus a plain reading of each idiom; use `languageNotes` when the node has it |
| Engineer new to this codebase | purpose, position, walkthrough, contracts, dependants, risks |
| Maintainer or reviewer about to change it | the same, plus invariants, failure modes, the tests that pin behaviour and the places that must change together |

### 2. Find the node

```bash
G=.ua/knowledge-graph.json
[ -d .understand-anything ] && G=.understand-anything/knowledge-graph.json
T='src/services/scheduler.ts:bookSlot'
jq -r --arg t "${T%/}" '
  ($t | split(":")) as $parts
  | .nodes[]
  | select(.filePath == $t or .name == $t
           or (($parts | length) == 2 and .filePath == $parts[0] and .name == $parts[1])
           or ((.filePath // "") | startswith($t + "/")))
  | [.id, .complexity, (.lineRange // [] | join("-"))] | @tsv' "$G"
```

Run it from the directory that holds the data folder (the repository root, or the analysed subdirectory of a monorepo). What comes back decides the next move:

- One line for a `path:name` target, or a file line followed by its members for a path: continue with the first id.
- Several unrelated ids for a bare name: show them and ask which one, or pick the one in the file the conversation is about and say so.
- A folder: the file-level ids under it. Explain the folder as a unit (step 6 has the shape) and offer a deep dive on its most connected file.
- Nothing: check the spelling against `jq -r '.nodes[].filePath // empty' "$G" | sort -u | grep -i scheduler`. A file that exists on disk but has no node is new, generated, or excluded by `.understandignore`. Explain it from source alone and state that the graph had no context for it.

### 3. Pull the dossier

Save this filter as `ua-dossier.jq`. It prints the target, its layer and tour steps, the symbols it contains, and its relationships in both directions. For a file it treats the file and its members as one unit, so calls made by its functions count as the file's own.

```jq
(.nodes | map({key: .id, value: .}) | from_entries) as $n
| $n[$id] as $me
| ([$id] + [.edges[] | select(.type == "contains" and .source == $id) | .target]) as $scope
| def inside: . as $x | $scope | index($x) != null;
  def links(f; g): [.edges[] | select(.type != "contains" and .type != "exports")
    | select((f | inside) and (g | inside | not)) | "  \(.type)  \(g)  -- \($n[g].summary)"] | unique[];
  "TARGET   \($me.id)  [\($me.complexity)]  \($me.filePath // "-") \($me.lineRange // [] | join("-"))",
  "SUMMARY  \($me.summary)",
  "TAGS     \($me.tags | join(", "))" + (if $me.languageNotes then "\nNOTES    \($me.languageNotes)" else "" end),
  (.layers[] | select(any(.nodeIds[]; $n[.].filePath == $me.filePath)) | "LAYER    \(.name): \(.description)"),
  (.tour[] | select(any(.nodeIds[]; inside)) | "TOUR     step \(.order): \(.title)"),
  "MEMBERS", ($scope[1:][] | "  \(.)  lines \($n[.].lineRange // [] | join("-"))  [\($n[.].complexity)]"),
  "RELIES ON", links(.source; .target),
  "USED BY", links(.target; .source)
```

```bash
jq -r --arg id 'function:src/services/scheduler.ts:bookSlot' -f ua-dossier.jq "$G"
```

Reading the two relationship lists: `RELIES ON` is every edge that starts at the target, `USED BY` every edge that ends at it. For `imports`, `calls`, `reads_from` and `writes_to` those headings are literally true. A `tested_by` line under `RELIES ON` names the test that covers the target, and a `documents` or `configures` line under `USED BY` names the document or config file that refers to it.

A function with an empty `USED BY` list is not necessarily unused. Import edges are recorded between files, so run the dossier for the file node as well before saying anything about callers.

### 4. Make sure the lines are still the lines

```bash
SHA=$(jq -r '.project.gitCommitHash' "$G"); FILE=src/services/scheduler.ts
git rev-parse --verify --quiet "$SHA^{commit}" >/dev/null && git diff --stat "$SHA" -- "$FILE"
git status --porcelain -- "$FILE"
```

No output means the file is as analysed, and `lineRange` can be trusted. If the file changed, find the symbol again with `grep -n "function bookSlot" "$FILE"` and treat the summary as a description of an older version. If the hash does not resolve, say that freshness could not be checked and rely on the source.

### 5. Read the code, numbered

```bash
nl -ba src/services/scheduler.ts | sed -n '23,31p'
```

Line numbers in the output are what the explanation cites. Read in this order, and stop when the reader's question is answered:

1. The target itself, completely. For a file over about 400 lines, read the exported symbols first, using the `MEMBERS` ranges, then the helpers they call.
2. The signature and the first lines of each `RELIES ON` callee, enough to know what it returns and what it can throw.
3. One real call site from `USED BY`, to see which arguments arrive in practice and how errors are handled by the caller.
4. The linked test, if any. Tests state the intended behaviour more plainly than comments do.

While reading, note three things the graph cannot know: what the code assumes without checking, what it changes outside itself (database, files, network, shared state), and what happens on the failure paths.

### 6. Write the explanation

For a function, class or single file, use these labelled blocks in this order (Example 1 shows one filled in):

| Label | Content |
|---|---|
| first line | the name, then one sentence saying what it is for in the project's own terms |
| `Where it sits` | layer, path with line range, and the entry point or caller it is reached from |
| `What it does` | numbered steps, each ending with the line or lines it describes |
| `Contract` | inputs and what they must satisfy, the return value, the errors it can raise |
| `Side effects` | writes, messages sent, state changed; or "none" |
| `Leans on` | each callee with what the target gets from it |
| `Leaned on by` | each caller with what it expects back |
| `Watch out` | unchecked assumptions, races and edge cases, with line numbers |
| `Tests` | the test file and the behaviour it pins, or "no test is linked to this file" |
| `Basis` | the graph's short hash and date, and the files and line ranges read in source |

For a folder, replace the walkthrough with a table of its files (path, role in one line, complexity), followed by how data moves between them, the folder's entry points from outside (the `USED BY` lines whose source is outside the folder), and the single file to read first.

Order matters: purpose before mechanics, mechanics before caveats. Keep quoted code to the lines being discussed. Name things as the code names them, and define a project term the first time it appears.

### 7. Check the result before sending

- Every sentence about behaviour points at a line that was read in step 5. Anything taken only from a summary is marked "according to the analysis".
- The summary and the code agree. Where they do not, the code wins, and the mismatch is reported: it usually means the graph is stale or the summary overstated.
- Each caller and callee named in the text exists in the source today (`grep -n "bookSlot" -r src tests`).
- The explanation answers what the user asked, not only what the template lists. "Why does this exist?" needs history or a design reason; if neither the code nor the documents give one, say that it is not recorded.

## Examples

### Example 1: `/understand-explain src/services/scheduler.ts:bookSlot`

Project `clinic-booking`, a Fastify booking API. Step 2 returns `function:src/services/scheduler.ts:bookSlot	complex	23-31`. The dossier:

```text
TARGET   function:src/services/scheduler.ts:bookSlot  [complex]  src/services/scheduler.ts 23-31
SUMMARY  Re-checks availability, inserts the appointment and queues its reminder.
TAGS     booking, write-path
LAYER    Booking Rules: Scheduling logic and reminders; the only place where availability is decided.
TOUR     step 3: Booking an appointment
MEMBERS
RELIES ON
  calls  function:src/db/appointments-repo.ts:insertAppointment  -- Inserts an appointment row and returns it.
  calls  function:src/services/reminders.ts:queueReminder  -- Schedules a reminder message ahead of the appointment start.
  calls  function:src/services/scheduler.ts:findFreeSlots  -- Walks the day in slot-sized steps and keeps the ones no booked appointment overlaps.
USED BY
  calls  file:src/routes/appointments.ts  -- POST /appointments books a slot (409 when taken); DELETE /appointments/:id cancels.
```

Step 4 prints nothing. After reading lines 23-31, the three callees and the route, the reply is:

```text
bookSlot: turns "this patient wants this start time with this practitioner" into a stored appointment, or refuses.

Where it sits   Booking Rules layer; src/services/scheduler.ts:23-31; reached from POST /appointments (src/routes/appointments.ts:13)
What it does    1. Cuts the calendar day out of the start time as a UTC date string (line 24)
                2. Recomputes that day's free slots for the practitioner (line 25)
                3. Looks for a free slot starting at exactly the requested millisecond (line 26)
                4. Throws SlotTakenError when there is none (line 27)
                5. Inserts the appointment using the slot's own start and end, never the caller's (line 28)
                6. Queues the reminder and returns the stored row (lines 29-30)
Contract        takes a practitioner id, a patient e-mail and a Date; returns the Appointment with its database id;
                fails with SlotTakenError, or whatever the database driver throws
Side effects    one row in appointments; one entry in the in-memory reminder queue
Leans on        findFreeSlots: the list of open intervals; insertAppointment: the write; queueReminder: the reminder time
Leaned on by    the POST /appointments handler, which maps SlotTakenError to HTTP 409 (line 16) and rethrows the rest
Watch out       lines 25-28 check and then insert with no transaction and no unique constraint in schema.sql, so two
                simultaneous requests for one slot can both succeed; a start time that is not on a 30-minute boundary
                is reported as "taken"; the reminder queue lives in process memory and is lost on restart
Tests           tests/scheduler.test.ts covers findFreeSlots only; nothing exercises bookSlot
Basis           graph 7c41e0d of 2026-09-28; read scheduler.ts 1-35, appointments.ts 10-19, reminders.ts 7-15, schema.sql in source
```

The graph called the function "complex" and summarised it as a re-check plus insert. The race between the check and the insert appears nowhere in the graph; it came from reading lines 25-28 next to the table definition.

### Example 2: A database table as the target

Request: "explain the appointments table and who touches it". Step 2 with `T=appointments` returns `table:src/db/schema.sql:appointments	moderate	6-13`. Its dossier has an empty `RELIES ON` and:

```text
USED BY
  reads_from  function:src/db/appointments-repo.ts:findByDay  -- Booked appointments of one practitioner on one day.
  writes_to  function:src/db/appointments-repo.ts:insertAppointment  -- Inserts an appointment row and returns it.
  writes_to  function:src/db/appointments-repo.ts:markCancelled  -- Sets status to cancelled for a booked appointment.
```

The explanation lists the six columns from `schema.sql:6-13`, states that all access goes through one repository file (confirmed with `grep -rn "appointments" src`, which finds no other SQL), describes the life of a row (inserted as `booked`, flipped to `cancelled`, never deleted), and flags that `status` is free text with no check constraint while the TypeScript type allows only two values.

### Example 3: The target is not in the graph

`/understand-explain src/services/waitlist.ts` returns no line. The file exists and was created two days after the graph's `analyzedAt`. The agent explains it from source, finds its importer with `grep -rn "waitlist" src`, labels the answer "explained from source only; this file is newer than the knowledge graph", and offers an incremental `/understand` so that the next question about it has context.

## Guidelines

- The graph is the map and the source is the territory. Summaries, tags, `complexity`, layer assignments and most `calls` edges were written by a model at analysis time; only what is read in step 5 is evidence.
- Do not paste the dossier as the answer. It is working material, and its summaries repeat what the analysis guessed.
- Small private helpers have no node: the analysis keeps exported or substantial symbols. Explain a helper as part of the function that calls it.
- A missing edge does not prove independence. Dynamic dispatch, dependency injection, framework conventions (routes registered by file name, decorators), reflection and SQL in strings leave no trace in the graph.
- Summaries are stored in the language chosen at analysis (`outputLanguage` in the data folder's `config.json`). Match descriptions against that language, and answer in the language the user writes in.
- Keep to one target per explanation. A request for "the whole backend" is an onboarding guide or an architecture overview, not a deep dive; propose a layer or a folder to start with.
- Never read `.env` files, key stores or other secrets to explain configuration. Describe which variables the code reads and where they are consumed.
- Skip the graph for a twenty-line script or a file the user has open and only wants a single line clarified. Reading it directly is faster and just as accurate.

Files in this skill

  • SKILL.md5.7 KB
  • _scores.json1.6 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…