agentleFS
non-nattawutnon-nattawut/Code-Archaeologist-LLM-Agent-Skill/.agents/skills/code-archaeologist/SKILL.md

code-archaeologist

Skill1 starsChanged 49 days agoMITView on GitHub

- Structure (class level) — who references/imports whom. data/structure/graph.json + data/structure/vault/<Entity>.md - Flow (method level) — who calls whom, so a real request path can be traced (OrderController.create_order -> OrderService.place_order -> OrderRepository.save). data/flow/flow_graph.json + data/flow/notes/<Class.method>.md data/ is grouped by map: structure/, flow/, report/<map>/ (the review pass), cache/ (AI-summary + freshness state; you rarely touch it). Both maps share one viewer, data/explorer.html, switched from its header. Paths. Every command below names the skill folder as .agents/skills/code-archaeologist. The installer rewrites that to…

  • Reads credentials
  • Installs packages

1 other project uses a version of this.

Adopted by

Most recently taken up by

  • Other versions1 project uses a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 1

    1. GarphenGate/moltline-oss★ 0same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repository2 other files it gives its agents.

The file

We split this skill into the rules it gives an agent

10 rules. About 0 are common, 4 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Paths: Every command below names the skill folder as .agents/skills/code-archaeologist.

    line 20
  2. Very specific:

    If instead it says "runtime found but not loadable", the wheels were installed under a different Python version than the one running now — …

    line 68
  3. Very specific:

    If a grammar is missing, the build still succeeds and those files simply produce no nodes.

    line 72
  4. Distinctive:

    NEVER read raw source for architecture, flow or review questions.

    line 84
  5. Very specific:

    Start from the graph, not from grep: search.py (Command 4) to find node ids, trace_path.py (Commands 5-6) for the path or blast-radius.

    line 87
  6. Distinctive:

    Use it for "review this codebase" / "where is the risk" / "what should we refactor first", then answer from the brief and the report — …

    line 424
  7. Very specific:

    Test files are recognized by convention across languages — test/, tests/, __tests__/, spec/ directories.

    line 447
  8. Distinctive:

    After any code change, rebuild the map (Command 3).

    line 490
  9. Very specific:

    Call resolution is heuristic, not type inference: self.<dep>.m() via __init__ hints or assignments, typed params/locals, same-class …

    line 522
  10. Distinctive:

    Every script resolves paths from the skill root, so it runs from any working directory.

    line 530

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

name: code-archaeologist description: Zero-RAG codebase navigation with two maps — a project-structure graph (which classes reference which) and a method-level flow graph (which method calls which, i.e. request/execution flow), plus a review pass (health grade, risk scan, git hotspots). Use to explain architecture, trace how a request flows through methods, find what breaks if a class/method changes, or review a codebase for smells, risky code and change hotspots.---

Skill: Code Archaeologist & Living Wiki Navigator

Overview

Zero-RAG codebase navigation from local graphs and Markdown notes. Two maps:

- Structure (class level) — who references/imports whom. data/structure/graph.json + data/structure/vault/<Entity>.md - Flow (method level) — who calls whom, so a real request path can be traced (OrderController.create_order -> OrderService.place_order -> OrderRepository.save). data/flow/flow_graph.json + data/flow/notes/<Class.method>.md

data/ is grouped by map: structure/, flow/, report/<map>/ (the review pass), cache/ (AI-summary + freshness state; you rarely touch it). Both maps share one viewer, data/explorer.html, switched from its header.

Paths. Every command below names the skill folder as .agents/skills/code-archaeologist. The installer rewrites that to wherever it installs the skill; if the folder was copied by hand (for example to .claude/skills/code-archaeologist), use that folder in every command instead.

Setup — preflight before the first build

Once per machine/checkout, before the first project / flow / both build.

1. Python 3.10+ — required. The pipeline is stdlib apart from tree-sitter, which step 2 covers.

python --version        # or python3; needs 3.10+

Older or missing: stop and tell the user, the skill cannot run.

2. tree-sitter — for every language, Python included. Grammars are installed per language, so install only what the repo actually contains. Do this yourself — don't ask the user to.

cd .agents/skills/code-archaeologist && python -c "import tree_sitter; print('runtime ok')"
python .agents/skills/code-archaeologist/scripts/core/grammars.py --install python typescript   # the repo's languages
python .agents/skills/code-archaeologist/scripts/core/grammars.py --install                     # or every grammar

These are wheels — no compiler, grammar bundled, one package per language — at the pinned versions the skill was tested against (grammars.PINS; --print shows the pip command instead of running it). Run it with the same python that runs the skill: the runtime wheel is built for one Python version. Languages: python, javascript, typescript, tsx, java, go, csharp, kotlin, rust, swift, scala, groovy, dart, c, cpp, ruby, php, elixir; the runtime is always included. Two mappings are worth knowing: tree-sitter-javascript covers .js and .jsx, and tree-sitter-typescript covers both .ts and .tsx (one wheel, two grammars — .tsx genuinely needs the second one).

tree-sitter-python is not optional if the repo has any Python. It used to be read with the standard library, so it needed nothing; it is now read the same way as everything else, and without that wheel a Python repo produces an almost empty graph — with a warning saying so.

Install it yourself — this does not touch the user's Python. --target vendor puts the wheels in <skill>/vendor, which the skill's .gitignore excludes: nothing enters the environment the user runs their own projects in, no version can collide with theirs, and deleting the skill folder removes every trace. Keep all three flags — --target is the whole point, --only-binary :all: fails loudly instead of trying to compile, and --no-cache-dir stops pip writing the wheels outside the skill folder.

The build tells you exactly what is missing and the exact command:

! csharp skipped: no tree-sitter grammar installed. Run `pip install ... --target <skill>/vendor tree-sitter-c-sharp` to enable it.

If instead it says "runtime found but not loadable", the wheels were installed under a different Python version than the one running now — re-run the install command above, which rebuilds vendor/ for this interpreter.

If a grammar is missing, the build still succeeds and those files simply produce no nodes. brief prints a SKIPPED block naming the language and the fix. Never answer a "what calls X", "is this dead code" or "how big is this codebase" question while a language is skipped without saying so — the graph is smaller than the codebase, and nothing else in the output makes that visible.

brief also prints an UNPINNED line when an installed grammar is not the version this skill was tested against. It is a warning, not a failure: the build is fine, but if that language's output looks wrong, the version gap is the first thing to suspect. Re-running the install command above puts it back on the pin.

Operating Principles

1. NEVER read raw source for architecture, flow or review questions. 2. Pick the map: structure for "how is this organized / who uses X"; flow for "how does a request travel / what calls what". 3. Start from the graph, not from grep: search.py (Command 4) to find node ids, trace_path.py (Commands 5-6) for the path or blast-radius. Ids are bare (place_order, OrderService.place_order) unless two files define the same name — then each definition is qualified by its file (tests_map.build, report.build). So find an id with search.py --name 'build$' rather than typing one from memory; and a call to a shared name from a file that does not define it was dropped, not guessed. 4. Then take the facts in ONE call with context.py --node <id> (Command 7). Read individual notes (data/structure/vault/<Entity>.md, data/flow/notes/<Class.method>.md) only when the pack is not enough, and only for nodes on the discovered path. 5. Preserve [[EntityName]] / [[Class.method]] wikilinks in answers so they stay navigable. 6. Check freshness before trusting a map (Command 9). If it says stale, rebuild first — see "Keeping the maps current". 7. For review questions ("is this healthy?", "where is the risk?", "what to refactor first?"), run the report (Command 14), read the brief (Command 1), and open data/report/<map>/architecture_report.md only for the detail the brief points at. 8. Edges are a lower bound — say so, and name the loss. In every language a call is drawn only when the receiver's type can be read from the source; anything else is dropped, never guessed. - What is trustworthy is the declarations; what is partial is resolution. So "nothing calls X" only ever means "nothing the extractor could resolve calls X". - A node names its own loss in precision: interface-dispatch (the call stops at an interface), overloads (the arguments did not say which overload was meant, so that call was dropped; every overload is its own node), name-matched (a call through an object with no declared type was dropped, and the graph defines that name — listed in untyped). - A call through an interface lands on the interface's own node (declaration: true) and stops there. Each implementation is joined to that node by an implements link, so traces and --impact-of reach every implementation -- but which one runs is still unanswered, unless the scanned source settles which Spring bean is injected (a @Qualifier, one @Primary, the only bean, a @Bean method): then the call links straight to that bean's method. A bean registered outside the scanned source (a library's auto-configuration) is invisible to that rule. - context.py and the report print the caveat for you and name the node's loss — pass it on, and say it plainly when a whole answer (dead code, blast radius, "who calls this") rests on it.

Pick the command: what the user asked → what to run

Find the row before reaching for a tool. Every question below is answered from artifacts, so the answer costs a command, not a source read.

The user asksRunMap
"explain this repo", "where do I start"1 briefboth
"where is X handled?", "find the Y class"4 search.py --nameeither
"who calls X?" / "what does X call?"4 search.py --calls / --called-byflow
"how does a request get from A to B?"5 trace_path.py --from --toflow
"how are these two classes connected?"5 trace_path.py --from --tostructure
"what breaks if I change X?"6 trace_path.py --impact-offlow
"what does this PR / my current diff affect?"6 --impact-of-diffflow
"tell me everything about X"7 context.py --nodeflow
"which tests cover X?"7 context.py --node → Covered byflow
"is this codebase healthy?", "what do we fix first?"14 report, then 1 briefboth
"any security problems?"11 scan_security.pyeither
"what's dead / rotting?", "TODOs?"15 debt.pyeither
"what's untested?"16 tests_map.pyflow
"what's been copy-pasted?"17 duplicates.pyflow
"what changes most / who owns this?"12 git_insights.pyeither
"how big / complex is X?"13 metrics.pyeither
"any cycles / layering problems?"10 analyze.pyeither
"show me the architecture" (for a human)8 build_html.py → explorer.htmlboth"is the map still accurate?"9 checkboth
anything, and the map is stale or missing2 / 3 project / flow / both—

Two rules that decide the Map column when it says "either":

- structure answers how is this organised, who uses this class — nodes are classes, components and module groups. - flow answers what calls what, how does a request travel — nodes are methods and functions.

Node ids differ between the two maps, so never carry an id from one into the other, and never read a report built for one map against the other.

Available Tool Commands

> First build in this project? Run the Setup preflight above first. > Paths below assume the default install; if the skill lives elsewhere, that prefix is already > rewritten to match.

1. Orientation brief — start here

Fixed-size digest of what is built: counts and grade per map, staleness, size, entry points, and the top longest / most complex / most churned / riskiest nodes. Costs the same on a 200-file repo as on a 5-file one, so use it instead of reading architecture_report.json.

python .agents/skills/code-archaeologist/scripts/archaeologist.py brief --src ./src

--map structure switches the detail sections, --top N sizes them, --json for tooling. --src is optional here too — without it the freshness line uses the roots the last build recorded.

2. Build the Project Structure map

python .agents/skills/code-archaeologist/scripts/archaeologist.py project --src ./src

Nodes are classes, React components and one <Name>Module page per file of module-level functions, from Python and JS/TS alike. A JS/TS function that returns JSX gets kind: component and layer: ui, and an import ... from "./x" -- or "@/x" through the tsconfig/jsconfig paths alias -- becomes an edge to whatever that file defines.

3. Build the Flow / Request-Flow map

python .agents/skills/code-archaeologist/scripts/archaeologist.py flow --src ./src
python .agents/skills/code-archaeologist/scripts/archaeologist.py both --src ./src   # both maps
python .agents/skills/code-archaeologist/scripts/archaeologist.py flow --src ./backend ./frontend

--src takes several roots, so a monorepo lands in one graph (node source keeps its root prefix: backend/…, frontend/…). Every language is parsed by tree-sitter (grammar missing → those files skipped with a warning). Frontend fetch/axios calls are matched to backend route handlers by HTTP method + path, giving cross-stack http edges — so one trace can run frontend → API → service → repository.

Route handlers are recognised in FastAPI, Flask (including @app.route on a module-level def and methods=["GET", "POST"]), Express (named handler or inline arrow), Nest (@Controller prefix + @Get/@Post suffix), Spring (Java and Kotlin), ASP.NET, Go routers and actix-web / Rocket — and, from a route table away from the handler, Django urlpatterns, Rails routes.rb, Laravel routes/*.php and Phoenix routers. A table route whose handler names no single node is not attached (the build says how many). A node's routes is a list — say all of them when asked what a handler serves. When a call's path does not match a route exactly, the link falls back to a route whose path is a suffix of it, and only when exactly one route matches; an ambiguous path is left unlinked rather than guessed, so a missing http edge means "could not tell", not "no caller".

4. Find the nodes (instead of grepping)

Filter the graph and get ids back for the commands below. Never grep source to find "where is X handled".

python .agents/skills/code-archaeologist/scripts/query/search.py --name "payment|charge"
python .agents/skills/code-archaeologist/scripts/query/search.py --doc "refund"
python .agents/skills/code-archaeologist/scripts/query/search.py --layer repository --kind method
python .agents/skills/code-archaeologist/scripts/query/search.py --calls OrderRepository.save
python .agents/skills/code-archaeologist/scripts/query/search.py --called-by OrderService.place_order
python .agents/skills/code-archaeologist/scripts/query/search.py --lang java --file controllers/
python .agents/skills/code-archaeologist/scripts/query/search.py --orphans --format json

Filters AND together; --graph picks the map (default: flow), --limit caps rows (default 40), --format json for tooling. --name and --doc take a regex; --file is a substring of the path.

--lang matches the tag on the node, which is coarser than the file census: py, js, java, go, csharp, kotlin, rust, swift, scala, groovy, dart, c, cpp, ruby, php, elixir. js covers all of .js, .jsx, .ts and .tsx — there is no ts or tsx node tag, so --lang ts returns no match even in a TypeScript repo. Use --lang js, or --file .tsx when you want one extension. (The finer tags ts/tsx/jsx do appear in metrics.json's file census and the explorer's language mix, which read the extension.)

--calls and --called-by are the two directions of one edge — "who calls X" and "what does X call" — and are the cheapest way to answer a connectivity question without a full trace. Ids are bare (place_order) unless two files define the same name, in which case each is qualified by its file (report.build), so search for an id rather than typing one from memory.

5. Trace Execution Flow

Structure is the default graph; flow needs --graph. Output is one line per path (A > B > C); add --all for every path, --format json only when something machine-reads it.

python .agents/skills/code-archaeologist/scripts/query/trace_path.py --from <SourceClass> --to <TargetClass>
python .agents/skills/code-archaeologist/scripts/query/trace_path.py \
  --graph .agents/skills/code-archaeologist/data/flow/flow_graph.json \
  --from OrderController.create_order --to OrderRepository.save

6. Blast-Radius / Impact Analysis

Everything upstream of a node — and, with --impact-of-diff, of a whole changeset (changed files are mapped to nodes, then their impact is unioned). Works on either graph.

python .agents/skills/code-archaeologist/scripts/query/trace_path.py --impact-of <ClassName>
python .agents/skills/code-archaeologist/scripts/query/trace_path.py \
  --graph .agents/skills/code-archaeologist/data/flow/flow_graph.json --impact-of <Class.method>
python .agents/skills/code-archaeologist/scripts/query/trace_path.py --impact-of-diff            # working tree
python .agents/skills/code-archaeologist/scripts/query/trace_path.py --impact-of-diff --staged
python .agents/skills/code-archaeologist/scripts/query/trace_path.py --impact-of-diff --base origin/main

7. Context pack for a node (one call, budgeted)

Everything known about a node from the artifacts: kind/layer/source, size and complexity, churn and owner, description, immediate callers and callees each with their own description, and the risks attributed to it. Replaces "trace, then open five notes".

python .agents/skills/code-archaeologist/scripts/query/context.py --node OrderService.place_order
python .agents/skills/code-archaeologist/scripts/query/context.py --node A B --depth 2 --max-chars 8000
python .agents/skills/code-archaeologist/scripts/query/context.py --diff     # every node the diff touches

--max-chars (default 6000) is a hard budget: neighbor lists shrink until it fits, and it says when it trimmed. --graph picks the map (default: flow), --format json for tooling. When tests call the node, the pack lists them under Covered by — real call edges, so it answers "which tests should I run for this change".

8. Regenerate the HTML explorer

archaeologist.py refreshes data/explorer.html on every build; this rebuilds it alone. When the user only wants the page created or replaced, the separate /code-archaeologist-explorer skill does that end to end (both maps, report, render).

python .agents/skills/code-archaeologist/scripts/query/build_html.py

One page holds both maps (header switch): grade, tiles and file tree on the left; seven views (Flowchart, which it opens on, then Graph, Treemap, Matrix, Tree, Cluster, Bundle) in the middle; FILE / PATTERNS / SECURITY tabs on the right. A map with no report still renders, minus the grade and review tabs. A Tests checkbox appears when the map has test nodes. It starts unchecked, so the page opens on the architecture without the suite hanging off it; tick it to draw the test nodes.

It is one self-contained file — data and the graph library are inlined, so it opens from file:// with the network off and can be emailed to someone with no repo access. Hand it to a human; it is not a way for you to read the graph (use Commands 4–7 for that). What to tell them it does:

- Left rail — health ring, a colour-by selector (layer / folder / churn / risk), stat tiles, language mix, a node search (by name or path), and a file tree that filters the canvas to a file's nodes and whatever they link to directly. Each section folds from its own heading to give the tree room, and both rails drag to resize from their inner border. - Centre — the seven views, folder hulls, and a blast radius toggle that shades everything reachable from the selected node. Freeze holds the current view: clicking another node still opens its details but no longer re-narrows the Flowchart or moves the canvas. In the Flowchart, a badge before each call's arrowhead numbers a node's calls in the order they are written -- not the order they run; 3a / 3b are the sides of one if/else, only one of which runs -- with a diamond for a call made only inside a branch and a ring for one inside a loop; where several callers meet at one box, the badge shown is the hovered or selected node's; Order hides them. The ⋯ overflow menu holds zoom in/out, fit and PNG export. - Right — the three tabs click through into each other (a risk opens its file, a pattern opens its node).

Dragging a node pins it where it is dropped, so a messy layout is fixed with the toolbar reset, which throws away the layout, the selection and the filter and re-runs it from scratch. Clicking empty canvas keeps the selection; reset is the way to clear it.

9. Check freshness (are the maps stale?)

Returns {stale, changed, added, deleted}. Rebuild if stale.

python .agents/skills/code-archaeologist/scripts/archaeologist.py check --src ./src
python .agents/skills/code-archaeologist/scripts/archaeologist.py check              # same roots as the last build

--src is optional: the build records its roots, so the check re-uses them. If a root has moved you get source root(s) not found from here rather than a false "everything was deleted".

10. Architectural smells & health grade

Cycles, orphans/dead nodes, backwards layer violations, high-coupling hubs, god objects, name-based idioms, and a 0-100 / A-F health score (--security <security.json> folds risk findings into the grade). Coupling is a direction, not a count. Each node's instability is I = fan_out / (fan_in + fan_out), and the three shapes a plain degree count folds together are reported apart: a hub (fan-in and fan-out both >= 5) is the one that hurts and the only one graded; a shared helper (fan-in >= 10, I <= 0.1 -- DateUtil.now) and a coordinator (fan-out >= 10, fan-in <= 2 -- a registration method) are normal shapes, reported so they stay visible and never deducted for. Say "40 things depend on this, and it depends on nothing" -- not "high coupling". A wrong-direction dependency (a stable node calling an unstable one) is graded: what everything relies on should not rest on what keeps changing. A method whose body every subclass replaces is listed under overridden, not as an orphan: say "never runs today", not "dead" -- removing it changes what a new subclass inherits. Code a framework calls is never an orphan: a node carrying entry (@Scheduled, @Bean, @Override, main, a Next.js next:page / next:route, module for a function a JS/TS or Python module calls as it loads, or init for one a field initializer, initializer block, constructor or Go package var calls) is called from outside the graph, like a route. A JS/TS function that is only passed (onClick={fn}, rows.map(fn), t.rich(key, { b: fn })) is reached by a passes link -- or is entry: module when top-level code hands it over (createApiInstance(fn)) -- whenever the name resolves through an import or to a function in the same file; --impact-of follows it, and it is never counted as a call. A field of a hook's result (const { api } = useSetdatVariant(); api.fetchX()) resolves when the types on the way are written: the hook's return type or the createContext<T> it returns, T's field, and a typeof someService it names. A function reaching its user through a prop or a variable of unknown origin still has no caller -- check for that before calling it dead. A const choosing a function (const save = id ? updateUser : createUser; save(x)) links to every branch as a call. Nor is any overload of a set some node lists in ambiguous (this::values, or a call whose arguments did not say which overload): one of them is called, and the graph cannot say which, so none is called dead. In the flow map a component's <Child /> is a renders edge, so --impact-of on a component reaches the pages that render it. A method that implements an interface (or overrides a base class in the graph) is reached through its implements link, so it is not an orphan, and --impact-of on it reaches the interface's callers.

A node's layer is declared by an annotation (@Service, @RestController) where there is one, else read from its name, else from its folder (service/pnodata/ -> service, response/ -> model, properties/ -> config) -- and a folder that names no layer leaves it unknown rather than guessing. Say which of the three answered when a layer decides an answer.

In the structure map a class is referenced by every type its users' source names -- a field, parameter or return type, any generic argument inside it, a local's declared type, X.class, an import, a name the same file defines (a component calling a function of its own module group), and the class a static constant is read from -- and a base class is used by the class that extends it. A class a framework builds and calls into (@SpringBootApplication, @Configuration, @Aspect, @ControllerAdvice, or one whose own method carries an entry such as @Scheduled) carries entry and is not an orphan. @Component / @Service / @Repository alone do not spare a class: Spring builds it, but one nothing injects runs nothing.

An orphan means "nothing in the source calls this", never "this cannot run". The graph states only what the code states -- the same standard an IDE's unused hint uses. So a function a framework finds by matching a name held in data is reported as an orphan, and that report is correct by this rule, not a miss: a next-intl t.rich tag function (const TAGS = { bold: ..., code1: ... }) whose name appears only inside a translation string in en.json; a bean or handler resolved from a string key; anything reached by reflection. Two independent reasons the graph cannot say otherwise -- the link is written in a .json (or config) file, which no parser of the code can see; and an object handed over whole (t.rich(key, { ...TAGS })) passes the object, so its members get no passes link of their own. Both are deliberate: guessing which member a data file names would invent links.

Report it that way, and do not "correct" it: say "nothing in the code calls it; a framework may still reach it by name at runtime -- check the message/config files before deleting". If a user or another agent calls such an orphan wrong because the string does appear in a JSON file, the answer is that both are true: it runs, and no code references it. This skill deliberately answers the second question.

python .agents/skills/code-archaeologist/scripts/review/analyze.py                    # structure (default)
python .agents/skills/code-archaeologist/scripts/review/analyze.py --format text      # no JSON envelope
python .agents/skills/code-archaeologist/scripts/review/analyze.py \
  --graph .agents/skills/code-archaeologist/data/flow/flow_graph.json

11. Risk / security scan

Line scan for hardcoded secrets, interpolated SQL, eval/innerHTML sinks and leftover debug statements. Each finding names the node owning the line, so it can be traced and blast-radiused (tests/fixtures/docs skipped, secrets redacted). A credential-named key whose value is only its own name, a path or prose (ACCESS_TOKEN: "accessToken") is not reported.

python .agents/skills/code-archaeologist/scripts/review/scan_security.py --src ./src

--graph <flow_graph.json> attributes findings to methods instead of classes; --out <file.json> saves the report.

12. Churn, ownership & hotspots (git)

Commits per file, top author per file, and a hotspot ranking where risk = commits x (1 + fan_in + fan_out). Outside a git repo it returns empty data with a note.

python .agents/skills/code-archaeologist/scripts/review/git_insights.py --src ./src --top 10

13. Size & complexity (lines of code)

Lines per file (total / code / comment / blank + language mix) and, for nodes in every graphed language, LOC, cyclomatic complexity, nesting depth and parameter count — keyed by the graph's node ids, so "how long / how tangled is OrderService.place_order" needs no file read, and the same question works on a Java or Go node.

python .agents/skills/code-archaeologist/scripts/review/metrics.py --src ./src --top 10

--graph <graph.json> ranks only that map's nodes; --out <path>.json saves it. Command 14 runs this for you into data/report/<map>/metrics.json.

A node's figures are measured on its own range (source + end) inside that file's parse, so they always line up with the id you queried. A node with no body has no figures and is listed in unmeasured_graph_ids rather than given a fake 1: a structure module group (which is a whole file) and a signature-only declaration: true member are the two normal cases. If a node you ask about is in that list, say it is unmeasured — do not read the source to substitute a number.

14. Full architecture report

One review pass per built map — census, grade, smells, anti-patterns, risks, hotspots, size and complexity, debt and test references — into data/report/<map>/architecture_report.md (+ .json, security.json, insights.json, metrics.json, debt.json, tests.json, duplicates.json). It also re-renders data/explorer.html with both reports embedded, enabling the health ring, churn/risk colors, ownership and the Patterns/Security tabs.

python .agents/skills/code-archaeologist/scripts/archaeologist.py report --src ./src

Use it for "review this codebase" / "where is the risk" / "what should we refactor first", then answer from the brief and the report — never from source.

15. Debt inventory (markers + dead code)

TODO / FIXME / HACK / XXX / BUG / DEPRECATED left in comments, each attributed to the node that owns the line, plus nodes nothing calls and files where every node is dead.

python .agents/skills/code-archaeologist/scripts/review/debt.py --src ./src --top 10

--graph picks the map, --out <file>.json saves it, --format json for tooling. Answers "what should we clean up first" without reading a file.

16. Test coverage map (by name)

Which nodes the test suite even mentions — and, more usefully, which it never names. A Class.method counts as referenced when a test file names both the class and the method; a function or class when the test names it.

python .agents/skills/code-archaeologist/scripts/review/tests_map.py --src ./src --top 15

Name-based, not execution coverage: no runner, nothing to install. A referenced node may still be untested, but an unreferenced one is a real gap. Dunder methods and nodes living in test files are left out of the count.

Test files are recognized by convention across languages — test/, tests/, __tests__/, spec/ directories; test_*.py, *_test.go, *.test.tsx, *_spec.rb; OrderServiceTest.java, FooTests.kt, PaymentIT.java, QuxTests.cs; and files carrying @Test / @SpringBootTest (JUnit 5, Spring Boot), [Fact] (.NET), #[test] (Rust) or func TestX(t *testing.T) (Go). Such nodes get layer: test and are never reported as dead code — a runner calls them, so nothing in the graph does. Their calls into production code are coverage, not coupling, so they are also left out of the hub / god-object counts; what they are used for is Command 7's "Covered by" and --impact-of, which then tells you which tests a change puts at risk.

17. Duplicate code clusters

Copy-pasted functions, found even when the identifiers were renamed. Every node's body is reduced to its token shape — identifiers become ID, literals LIT, comments and whitespace vanish, keywords and operators stay — and bodies with the same shape are the same code.

python .agents/skills/code-archaeologist/scripts/review/duplicates.py --src ./src --top 10

A rename cannot hide a copy; changing an operator or adding a branch does split the pair, which is the point — it finds copies, not merely similar-looking code. Bodies under 30 tokens are ignored so getters and one-line delegates do not flood the list. It errs toward missing a copy rather than inventing one: a docstring counts as a token, so documenting one copy and not the other hides the pair. Treat a cluster as a prompt to look, not proof of a bad abstraction.

The output has two lists, and they answer different questions:

- clusters — whole bodies that reduce to the same shape. "These functions are the same function." - blocks — a run of 30+ tokens copied into two or more otherwise different nodes, which no whole-body hash can see. Each entry is one copied block with every places it occurs in (node id + line range), so one block in five places is one finding, not ten pairs. Use it for "this validation stanza was pasted into six handlers".

A pair already reported as a cluster is never repeated as a block. Blocks are trimmed to whole lines in both copies, so a copy laid out differently line by line is missed; nodes with declaration: true are skipped entirely (a signature has a range but no body to compare); and a fingerprint appearing in 50+ places is treated as boilerplate, not a copy.

Keeping the maps current (hybrid AI descriptions)

Graph structure is always extracted by AST — exact, zero tokens. Method descriptions resolve cheapest-source-first: docstring → cached AI summary (valid while the source hash is unchanged) → deterministic fallback. So the AI writes a summary only for methods that are new or changed and have no docstring.

After any code change, rebuild the map (Command 3). If it reports pending descriptions:

1. Open data/cache/pending_descriptions.json (each entry carries signature + code) and write a one-line summary per method as JSON { "<Class.method>": "<summary>", ... }. 2. Apply them, then rebuild to fold them in:

python .agents/skills/code-archaeologist/scripts/extract/apply_descriptions.py --input <summaries.json>
python .agents/skills/code-archaeologist/scripts/archaeologist.py flow --src ./src

Summaries are cached in data/cache/descriptions.json by source hash, so unchanged methods are never re-described; deleted ones are pruned (except after a build that skipped the frontend, which keeps the cache intact — those nodes are missing, not gone). 0 pending means done.

Notes

- Graphs cover seventeen languages: Python, JS/TS/JSX/TSX, Java, Go, C#, Kotlin, Rust, Swift, Scala, Groovy, Dart, C, C++, Ruby, PHP and Elixir. A language whose grammar is not installed is skipped by name — the build says so and brief prints a SKIPPED block with the command — and the file-level passes (lines, risk scan, debt markers, test detection) still read it. - One parser for every language: tree-sitter, whose wheels install into <skill>/vendor and never into the user's Python. Node is not used at all. The generated HTML needs no network: the graph library is vendored and inlined, so the explorer opens from file:// offline. - Field values (kind, layer, lang, desc_source, and the review severity/rule/grade sets) live in scripts/core/taxonomy.py and scripts/review/scan_security.py — see templates/TAXONOMY.md for the allowed values, and edit those rather than individual pages. - Dependency and build directories are never scanned, so they are absent from the graph by design, not by omission: .git, __pycache__, venv, .venv, node_modules, .idea, dist, build, .next, .nuxt, .svelte-kit, .angular, .turbo, .parcel-cache, .gradle, .dart_tool. If asked why a library or a generated file is missing, that is the reason. Note target, out, coverage, vendor and data are not skipped — each is a real source directory in some ecosystem (data is a Next.js route segment) — so those may appear. - Call resolution is heuristic, not type inference: self.<dep>.m() via __init__ hints or assignments, typed params/locals, same-class self.m(), a cast receiver (((UserSecurity) u).getPlantIds()), a pattern variable (u instanceof UserSecurity us, case UserSecurity us ->, C# u is Session s), and the Java inside a MapStruct @Mapping(expression = "java(...)"). Unresolved calls (libraries, stdlib) become no edge and are counted per node as ext ("N ext" in the explorer). - resolve_descriptions() in build_flow.py is where a description is chosen — the hook for richer summaries. - Every script resolves paths from the skill root, so it runs from any working directory.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.