brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
understand-chatAnswers questions about a codebase from the knowledge graph built by the open-source Understand-Anything plugin, and covers installing the plugin and producing that graph. Use when someone asks "how does checkout work in this repo", "what calls this function", "where is rate limiting handled", "what depends on this module", "run /understand-chat", "set up U…
Independent GitHub snapshot · Automated classification · Original documentation governs
Understand-Anything is an MIT-licensed plugin for coding agents (2.9.7 on its main branch when this was written). Its /understand command parses a project with tree-sitter, has the host model describe every file, and saves one JSON file, the knowledge graph. The other commands read that file: /understand-chat answers a free-form question, /understand-dashboard draws the graph, /understand-diff, /understand-explain and /understand-onboard cover change impact, deep dives and newcomer guides.
This skill is the question-answering job done carefully: locate or build the graph, query it as data, follow the relationships, then confirm in the source before saying anything specific. It also holds the installation steps for the whole command family.
The graph lives at .ua/knowledge-graph.json. Projects analysed before the directory was renamed keep .understand-anything/, and when that older directory exists every tool uses it. What the file holds:
| Key | Contents |
|---|---|
project | gitCommitHash and analyzedAt (the commit and time of analysis), name, description, languages, frameworks |
nodes | one entry per file, function, class, config, document, table, endpoint and so on: id, type, name, summary, tags, complexity (simple, moderate or complex), usually filePath, for functions and classes lineRange |
edges | source and target node ids, a type such as imports, calls, contains, tested_by, configures, documents, and a weight between 0 and 1 |
layers | named architectural groups, each listing its members in nodeIds |
tour | ordered walkthrough steps (order, title, description, nodeIds) |
Node ids start with the type: file:src/server.ts, function:src/lib/money.ts:toCents, class:src/services/label-service.ts:LabelService, config:package.json.
Claude Code, from inside a session:
/plugin marketplace add Egonex-AI/Understand-Anything
/plugin install understand-anything
Other hosts use the installer script in the repository. Clone it, read it, then run it with the host's id:
git clone https://github.com/Egonex-AI/Understand-Anything.git ~/.understand-anything/repo
less ~/.understand-anything/repo/install.sh
~/.understand-anything/repo/install.sh codex
Accepted ids: gemini, codex, opencode, pi, openclaw, antigravity, vibe, vscode, hermes, cline, kimi, trae, nanobot, kiro. The script symlinks the plugin's skills into that host's skills folder and links the plugin itself at ~/.understand-anything-plugin; restart the host afterwards. install.sh --update pulls a newer version, install.sh --uninstall codex removes the links. Cursor and VS Code with Copilot pick the plugin up from an opened clone, and Copilot CLI takes copilot plugin install Egonex-AI/Understand-Anything:understand-anything-plugin.
The project README also shows a one-liner that streams the installer from the network straight into a shell. Do not use it: the clone above gives the same result with a script that can be inspected first.
Codex calls skills with a dollar sign ($understand-chat), most other hosts with a slash. In Claude Code the fully qualified form is /understand-anything:understand-chat.
G=.ua/knowledge-graph.json
[ -d .understand-anything ] && G=.understand-anything/knowledge-graph.json
jq '{project: (.project | {name, gitCommitHash, analyzedAt}), nodes: (.nodes | length),
edges: (.edges | length), layers: [.layers[].name], tourSteps: (.tour | length)}' "$G"
Run this from the repository root. Inside a git worktree, look in the main checkout as well, because /understand writes there on purpose. The data directory is meant to be committed, so a fresh clone may already contain a graph.
No file means the project has not been analysed. Tell the user what the analysis costs before starting it: the first /understand reads the whole codebase with the host's model, can use a large number of tokens, and above 100 files stops to suggest a narrower scope. Later runs re-analyse only files whose structure changed.
| Invocation | Effect |
|---|---|
/understand | full analysis the first time, incremental afterwards |
/understand services/billing | analyse one subdirectory of a large monorepo; the graph is then written inside that subdirectory |
/understand --exclude "fixtures/*,docs/*" | skip paths, on top of the .understandignore file in the data directory |
/understand --language ja | write summaries and tour text in another language |
/understand --full | rebuild from scratch |
/understand --auto-update | refresh the graph after commits the agent makes in Claude Code |
Building needs Node.js 22+, pnpm 10+ and Python 3 (called as python) on the machine; the plugin compiles its core package on first use.
SHA=$(jq -r '.project.gitCommitHash // empty' "$G")
git cat-file -e "${SHA}^{commit}" 2>/dev/null &&
git diff --name-only "$SHA" HEAD -- . ':(exclude).ua' ':(exclude).understand-anything'
git status --porcelain -- . ':(exclude).ua' ':(exclude).understand-anything'
Empty output from both commands means the graph describes the working tree. If files are listed, compare them with the area the question is about. Unrelated drift needs one sentence in the answer. Drift in the files you are about to cite means reading those files directly and offering an incremental /understand. A hash that git cannot resolve (shallow clone, rewritten history) gets a plain "freshness unknown" note, not a refusal.
| Question | First query |
|---|---|
| "How does X work?" | score nodes for X and its synonyms, then walk calls and imports outward from the best hits |
| "What uses X?", "what breaks if X changes?" | edges whose target is X or the file containing X |
| "Where is X?" | match on name and filePath only |
| "How does a request get from A to B?" | shortest directed path between the two ids |
| "Give me the big picture" | layers with their descriptions, then tour in order |
| "What is risky or untested?" | nodes with complexity complex; files that appear in no tested_by edge |
Pick three to six search terms from the question, including the words the codebase is likely to use ("billing" for "payments", "guard" for "permissions"), and rank the hits. A name match outweighs a tag match, which outweighs a word in a summary:
jq -r --arg q 'quote|pric|carrier' '
[ .nodes[]
| { id, complexity,
score: ( (if (.name | test($q; "i")) then 3 else 0 end)
+ (if (.tags | join(" ") | test($q; "i")) then 2 else 0 end)
+ (if (.summary | test($q; "i")) then 1 else 0 end) ) }
| select(.score > 0) ]
| sort_by(-.score) | .[:8][] | "\(.score) \(.id) [\(.complexity)]"' "$G"
Nothing found: list the vocabulary the graph does have with jq -r '[.nodes[].tags[]] | unique | join(", ")' "$G", retry with the nearest terms, and if it is still empty say the graph has no node for the topic and fall back to searching the source.
ID='function:src/services/rate-engine.ts:quoteParcel'
jq -r --arg id "$ID" '
.edges[] | select(.source == $id or .target == $id)
| if .source == $id then "\(.type) -> \(.target)" else "\(.type) <- \(.source)" end' "$G"
Read every edge as "source does this to target". imports, calls, depends_on, inherits and implements point from the dependent to the thing it relies on, so the arrows coming into a node are the code that would notice a change. contains and exports point from a file to its own members and are not dependencies. tested_by runs from production code to its test. configures, documents, deploys, migrates and defines_schema start at the non-code file.
Function nodes rarely carry imports edges; those sit on the file node. Query both the symbol and its file before concluding that nothing uses it.
For a route between two nodes, save this as ua-path.mjs and run node ua-path.mjs "$G" file:src/server.ts table:src/db/schema.sql:zones:
import { readFileSync } from "node:fs";
const [file, from, to] = process.argv.slice(2);
const { edges } = JSON.parse(readFileSync(file, "utf8"));
const next = new Map();
for (const e of edges) {
if (e.type === "contains" || e.type === "exports") continue;
if (!next.has(e.source)) next.set(e.source, []);
next.get(e.source).push(e);
}
const seen = new Map([[from, null]]);
const queue = [from];
while (queue.length && !seen.has(to)) {
const id = queue.shift();
for (const e of next.get(id) ?? []) {
if (!seen.has(e.target)) { seen.set(e.target, e); queue.push(e.target); }
}
}
if (!seen.has(to)) { console.log("no directed path"); process.exit(1); }
const hops = [];
for (let e = seen.get(to); e; e = seen.get(e.source)) hops.unshift(`${e.source} --${e.type}--> ${e.target}`);
console.log(hops.join("\n"));
The graph is an index, not evidence. Import edges come from the parser. Summaries, tags, layers and most calls edges were written by a model. Before the answer names a behaviour, open the code:
jq -r --arg id "$ID" '.nodes[] | select(.id == $id) | "\(.filePath) \(.lineRange // [] | join("-"))"' "$G"
sed -n '12,18p' src/services/rate-engine.ts
For "what uses X" questions, also search the source for the symbol (grep -rn "toCents" src tests). The graph only creates nodes for exported or substantial functions, so small private helpers and some callers are missing from it.
path:lines, one line on what happens there.Project parcel-desk, an Express API. The ranking query from step 4 returns function:src/services/rate-engine.ts:quoteParcel with score 6, then file:src/routes/quotes.ts with 4. The neighbour query on the function prints:
contains <- file:src/services/rate-engine.ts
exports <- file:src/services/rate-engine.ts
calls -> function:src/db/zones.ts:findZone
calls -> function:src/lib/money.ts:toCents
calls -> function:src/lib/money.ts:addSurcharge
calls <- function:src/services/rate-engine.ts:cheapest
calls <- file:src/routes/quotes.ts
After reading lines 12-18 of the file, the reply is:
A quote is computed in
quoteParcel(src/services/rate-engine.ts:12-18, layer "Pricing and Booking"). For each of the two carriers it adds the carrier's base fee to its per-kilogram rate times the parcel weight, converts that to integer cents withtoCents, raises it by the zone's surcharge percentage withaddSurcharge(both insrc/lib/money.ts), and returns the list sorted cheapest first.
src/routes/quotes.ts:POST /quotesvalidates the body and callsquoteParcel.src/db/zones.tsfindZone: looks the surcharge up by the first two characters of each postcode; an unknown pair means no surcharge.cheapestin the same file takes the first element, which label booking relies on.Graph built at commit 3f9c2ab on 2026-10-01; no files changed since. Next: "what happens when a label is booked?", "which code reads the zones table?"
Neighbours of function:src/lib/money.ts:toCents show one caller, quoteParcel. Neighbours of the file node add imports <- file:src/services/rate-engine.ts, imports <- file:tests/rate-engine.test.ts and tested_by -> file:tests/rate-engine.test.ts. A source search finds a second call the graph has no edge for: the test asserts toCents(4.205) equals 421.
One production caller:
quoteParcel(src/services/rate-engine.ts:15), so every quote and every booked label price moves with the rounding.tests/rate-engine.test.ts:4pins the current behaviour and will fail if half-cents stop rounding up. Nothing else importssrc/lib/money.ts. The test call came from searching the source; the graph links the test to the file but not to the function.
jq reports that .ua/knowledge-graph.json does not exist and there is no .understand-anything/ directory. The agent does not start an analysis on its own. It reports that the project has 1,240 tracked files, that a first /understand will read all of them with the session's model, and offers three options: analyse only services/billing, where the question points; analyse everything; or answer this one question by reading the source directly.
jq or a script. Text search over a pretty-printed graph returns lines without the object they belong to, and reading a multi-megabyte graph whole wastes context for no gain..understandignore leave no edge.complexity is the analysing model's three-level judgement, not a measured metric. Treat it as a hint about where to read carefully.outputLanguage in the data directory's config.json). Search in that language./understand --auto-update works through Claude Code hooks: it reacts to commits made by the agent in a session and to session start. Commits made elsewhere leave the graph behind until the next run./understand-domain, and for a Markdown wiki /understand-knowledge; they write their own graphs. For a one-line lookup in a small repository, searching the source directly is faster than any graph.