repo-tools
repo-tools is one command-line tool with these subcommands for repositories:
| Subcommand | Job |
|---|---|
map | Writes the dependency graph and the architecture reports of a TypeScript/JavaScript, Python, C# or Rust repository. |
depgraph | The deprecated alias of map (through 2.x). |
check | Checks the ## Verification tables of Markdown documents against a fresh graph, and fails on drift. |
docs | Measures the doc comments of TypeScript, JavaScript, Python, C#, Rust, Go, Java, Kotlin, C, C++, PHP and Ruby files, plans the missing ones, and gates the result. |
chunk | Splits a large file into chunks, merges the chunks back, and shows which chunks changed. |
compress | Writes a compact copy of a file for a model context, and restores it. |
query | Answers structural questions from the graph of map, and writes two derived reports. |
clones | Lists the functions of a TypeScript or JavaScript repository, and finds the functions that copy each other. |
ste | Checks Markdown or docstring prose against Simplified Technical English. |
Status: version 2.6.0.
map,checkandqueryread their settings fromrepo-tools.config.jsonin the root. The section ofmapismap(its old name,depgraph, is also read).--config=<file>makes any of the three read another file.clonesreads its settings from theclonessection of the same file. A flag overrides the file, and the file overrides the default.repo-tools depgraphprints one deprecation line and runsmap. It accepts the 1.x scan-scope flags (--src,--tests,--exclude,--also-exclude,--all,--reachable-only,--include-tests) with a warning, because they have no effect in 2.0.0.mapexits 1 on them.compress -drestores JSON files only.
Install
Install one of the two forms.
npm package
The npm package needs Node 20 or later:
npm i -g @danielsimonjr/repo-tools
repo-tools --version
Compiled executable
The executable holds the tool and the Bun runtime. It needs no Bun, Node or node_modules.
Download the file for your platform and SHA256SUMS from the
GitHub release:
| Platform | File |
|---|---|
| Windows x64 | repo-tools-windows-x64.exe |
| Linux x64 | repo-tools-linux-x64 |
| macOS arm64 | repo-tools-darwin-arm64 |
Verify the file against SHA256SUMS before you run it:
# Linux
sha256sum --ignore-missing -c SHA256SUMS
# macOS: compare the output with the line of the file in SHA256SUMS
shasum -a 256 repo-tools-darwin-arm64
# Windows (PowerShell): compare the output with the line of the file in SHA256SUMS
Get-FileHash repo-tools-windows-x64.exe -Algorithm SHA256
On Linux and macOS, a downloaded file has no execute bit. The executable is not signed, so macOS also marks a file that a browser downloads. Run these commands before the first run:
chmod +x repo-tools-linux-x64 # or repo-tools-darwin-arm64
xattr -d com.apple.quarantine repo-tools-darwin-arm64 # macOS only
Run from source
Run the tool from source with Bun:
bun install
bun src/bin.ts --help
Build an executable
The compiled executable is the main product: one file that holds the tool and the Bun runtime.
It needs no Bun, Node or node_modules on the machine that runs it.
Prerequisites: Bun 1.4.2 or later, and a clone of this repository.
-
Install the dependencies from the lockfile:
bun install --frozen-lockfileDo this after every pull. The executable bundles the installed dependencies, so the build first compares
node_moduleswithbun.lock. On a difference, the build stops with exit 1 and names each package. A frozen install does not remove a package thatbun.lockno longer names. When the build names such a package, removenode_modulesand install again. -
Compile the executable for this computer:
bun run compileTo compile for another platform, give the target:
bun scripts/build.ts --compile --target bun-linux-x64The file goes into
bin/. Use--outdir <dir>to choose another folder.Target File Size (about) bun-windows-x64bin/repo-tools-windows-x64.exe108 MB bun-linux-x64bin/repo-tools-linux-x64103 MB bun-darwin-arm64bin/repo-tools-darwin-arm6484 MB -
Run the executable:
bin/repo-tools-windows-x64.exe --version bin/repo-tools-windows-x64.exe map --root=path/to/repo -
Test the executable, not only the source. The executable is the artifact that ships:
bun run smoke -- bin/repo-tools-windows-x64.exe
bun run build writes a second channel, the Node bundle dist/cli.js (node dist/cli.js --help),
with the tree-sitter grammar files (dist/*.wasm) next to it.
Reports
repo-tools map writes its reports into docs/architecture/ under the root (--out=<dir>
names another folder; --out=../reports writes outside the root). Every file is generated: do
not edit it by hand. Run repo-tools map again to update it. Each Markdown report starts with
a "GENERATED FILE -- do not edit by hand" banner.
The census is the set of source files that git tracks (outside a git work tree, a pruned walk).
The language with the most files decides how map reads the repository. Input files never come
from the output folder. The duplicate allowlist, the duplicate baseline and the coverage policy
are in docs/architecture/, or at the path that the config gives. A repository that already has
its own allowlist must set map.duplicateAllowlist (the old key depgraph.duplicateAllowlist
is also read) to that file. Mathts has 283 allowlisted names. With the default path, the
duplicate gate fails on those names.
A literal relative import() is a graph edge. The Python repo_map.py records none. The parity
record in docs/parity-2.0.0.md gives the rules and the verdict.
| File | What it answers |
|---|---|
dependency-graph.json | The core graph: each file by area, with its imports, exports and edges; the reachability; the cycle counts; and the statistics. |
file-inventory.json | Where is each source file, and what is it? Package, area and disposition (reachable, build entry, test-only, orphan, test, tool, config, example, bench), and the links that the census did not follow. |
duplicate-symbols.json | Which names do two or more files define? For TypeScript, also the classified lists. |
unused-analysis.json | Which exports and files does nothing import? |
dependency-layers.json | The subsystem view: the src files by module, the entry points, the layers and the cyclic components. |
DEPENDENCY_GRAPH.md | How do the files and modules depend on each other? Per-file imports and exports, the dependency matrix, cyclic components (runtime and type-only), a Mermaid diagram, and statistics. |
dependency-graph.yaml | dependency-graph.json as YAML. |
dependency-summary.compact.json | A small summary of the graph, with short keys, to give to a model. |
FILE_INVENTORY.md, duplicate-symbols.md, unused-analysis.md | The three JSON reports above, for a person to read. |
TEST_COVERAGE.md | Which source files does a test load (directly, through a barrel, through a side-effect import, or by import()), and which files does no test reach? |
test-coverage.json | The same coverage data, with the test-to-source map. |
package-export-surfaces.json | Which names does each package export publicly? Not written for C#. |
docs/parity-2.0.0.md records how these reports compare with the Python tool and with
depgraph 1.x.
repo-tools query --emit writes two more files into the same folder. They are also generated.
| File | What it answers |
|---|---|
dependency-reverse.json | Which files import each file? The reverse edges of the graph. |
node-safety.json | Which packages are browser-safe, which files import a node: builtin, and which of those files does the . entry of each browser-safe package reach? |
Check the documents against the graph
repo-tools check --docs=<dir> reads every *.md file of the folder. The check reads the
## Verification table of each document. Each row | claim | value | source | names a metric of
the graph (totalFiles, orphanedFiles, runtimeCircularDeps and so on). The value is the
number that the document states. The check builds the graph again, so a stale report cannot hide
drift. A relative --docs path is relative to the root.
## Verification
| Claim | Value | Source |
|---|---|---|
| totalFiles | 243 | file-inventory.json |
repo-tools check --docs=docs/architecture
# check passed: every claim matches the graph (1 claim in 1 document).
When a value drifts, the exit code is 1, and standard error lists every problem, one per line:
OVERVIEW.md: totalFiles claims 243 but actual is 244
A document passes without a check when one of its lines is exactly
<!-- repo-map:no-verification -->. The marker must stand alone on its line: a marker inside a
sentence, a code span or a table cell does not count.
repo-tools map writes that line at the top of each generated report. Every other gap is a
failure. A missing docs folder, a folder with no *.md file, a document without a Verification
section, and a section without a row are failures. An unknown claim name is a failure too. So is a
metric that the graph build declared unreliable. docs/design.md (section 15) gives the rules and the
differences from the Python tool. docs/parity-check.md records the comparison with that tool on
11 repositories.
Gate the documentation comments
repo-tools docs reads the TypeScript, JavaScript, Python, C#, Rust, Go, Java, Kotlin, C, C++, PHP and Ruby files that git tracks. It
finds each function, class, interface, type alias and method, and the doc comment of each one. In
C# and Rust it also finds each struct, enum, trait and property. It needs no Python, .NET or Rust toolchain on
the computer.
repo-tools docs scan . # writes docs/code-docs/COVERAGE.md and coverage.json
repo-tools docs check . # the gate: exit 1 on a MUST issue
repo-tools docs stub . --path=src/api # a dry run: lists the stubs and writes nothing
repo-tools docs stub . --path=src/api --apply
repo-tools docs stub . --style=numpy --apply # Python: google (default), numpy, rest
The stub style of a C# file is xmldoc (/// <summary>), and of a Rust file rustdoc (/// with
# Arguments and # Returns sections). The stub style of a Go file is godoc: one // line that
starts with the name. The stub style of a Java file is javadoc, and of a Kotlin file kdoc. Both
write @param and @return. The stub style of a C or C++ file is doxygen. It writes a @brief line,
@param and @return. The stub style of a PHP file is phpdoc (@param $x). The stub style of a Ruby
file is yard (# lines with @param x and @return). A C or C++ header (.h, .hpp, .hh, .hxx) has
exported symbols. A source file (.c, .cpp, .cc, .cxx) has none, because the header is the contract.
The rules are:
| Rule | Tier | The rule |
|---|---|---|
| M1 | MUST | An exported symbol has a doc comment. A test file is exempt. |
| M2 | MUST | The doc comment has a summary line. |
| M3 | MUST | The doc comment names no parameter that the signature lacks. |
| M4 | MUST | The doc comment holds no TODO: marker. |
| M5 | MUST | A file uses one doc dialect: google, numpy or rest for Python, tsdoc or jsdoc for TypeScript, xmldoc for C#, rustdoc for Rust, godoc for Go, javadoc for Java, kdoc for Kotlin, doxygen for C and C++, phpdoc for PHP, yard for Ruby. |
| S2 | SHOULD | The summary ends with a full stop. |
| S5 | SHOULD | The doc comment does not repeat a type that the signature holds. |
| S6 | SHOULD | The prose follows Simplified Technical English (the rules of repo-tools ste). |
Only a MUST issue changes the exit code of check. A file that does not parse is also a failure,
because an unknown file is not a clean file. A repository with no source file fails too. So does a
repository with more files of another language (Swift, for example) than of all the languages
above together. The command names the language and exits 1.
stub is a dry run until you pass --apply. Each stub holds the marker TODO:, so check fails
until you replace the stub with real prose. The command parses a file again after the change.
A file that would no longer parse stays unchanged. The command skips a file that is a link or
resolves outside the root, and it names the skipped file.
--style=<style> selects the form of the stubs: tsdoc (default) or jsdoc for TypeScript and
JavaScript, google (default), numpy or rest for Python. --style=auto follows the one dialect
of each file.
To gate a pull request and not the whole backlog, give check the changed files:
git diff --name-only origin/main... > changed.txt
repo-tools docs check . --paths-from=changed.txt
A .code-docs.json file at the root excludes paths. Each entry needs a reason, and the output
names each exclusion:
{ "exclude": [ { "path": "assembly/", "reason": "AssemblyScript" } ] }
docs/design.md (section 16) gives the rules, the exit codes and the differences from the Python
tool. docs/parity-docs.md records the comparison with that tool on 9 repositories.
Query the graph
repo-tools query answers questions from dependency-graph.json (and
package-export-surfaces.json for is-public). It parses no source file, so run
repo-tools map first. It reads each area of the graph, and a path that is not a file of the
graph exits 1. The examples use a copy of tests/fixtures/depgraph/mono-repo:
repo-tools map
repo-tools query dependents packages/core/src/math.ts
# packages/core/src/index.ts
# packages/core/src/worker.ts
# packages/core/tests/math.test.ts
repo-tools query symbol-users add
# packages/cli/src/main.ts
# packages/core/src/index.ts
# packages/core/tests/math.test.ts
repo-tools query is-public packages/core add
# PUBLIC: add is exported from packages/core
repo-tools query is-public packages/core double
# INTERNAL: double is not in the public export surface of packages/core
repo-tools query cycles
# 0 simple cycles
repo-tools query cycles --components
# runtime: 0 cyclic components
# type-only: 0 cyclic components
The browser-safety commands serve TypeScript/JavaScript only. A package is the folder above a
src/index.ts file (. for the root file). Each package is
browser-safe, unless --node-runtime=<pkg,...> or the config key query.nodeRuntimes lists it.
The . entry of a browser-safe package must not reach a file that imports a node: builtin:
repo-tools query node-safety
# packages/core: clean (the . entry reaches no node: code)
repo-tools query --check-browser-safety
# browser-safety check passed: the . entries of 1 browser-safe package reach no node: code.
repo-tools query --emit
# Written: docs/architecture/dependency-reverse.json (4 files)
# Written: docs/architecture/node-safety.json (0 node files, 0 leaks)
--check-browser-safety exits 1 on a leak. The query reads its reports from --out, else from
the config key query.out, else from map.out, else from docs/architecture. Run
repo-tools query --help for every option.
Find the functions that copy each other
repo-tools clones lists the functions of a TypeScript or JavaScript repository. It finds the
functions that copy each other. The command only reads the repository. It writes files only when
you name an output folder.
repo-tools clones . # prints the summary and writes no file
repo-tools clones . --src=src --out=reports/clones
repo-tools clones . --min-nodes=30 --near=0.7
A function is a unit when it has a name and its body has at least 50 labels. A label is one node of the body. Names, literals, types and comments give no detail, so a copy with other names still matches. The command finds two kinds of result:
| Result | Meaning |
|---|---|
| Exact group | Units with the same label sequence. |
| Near pair | Two units that share enough runs of 6 labels. |
--src=<path>... names the folders to read. The default auto reads the files of the src area.
--out=<dir> writes inventory.jsonl (one line for each unit) and clones.json (the groups and the
pairs) into a folder. Give the folder as a path relative to the root. Run repo-tools clones --help for every limit.
Exit codes: 0 when the run finished, even if it found no copy. 1 when the root holds no source file to read. 2 on a usage error, a bad config file or a root that is not a folder.
The result is a list of candidates. Read both bodies of a pair before you change anything.
The command serves the owner of one repository during a refactoring. A score ranks the pairs of one run. Do not compare counts or scores between repositories, settings or tool versions.
docs/design.md (section 17) gives the rules. docs/parity-clones.md records the comparison with the
prototype of the memoryjs audit, on memoryjs and on deepthinking-mcp. The record shows that the two parsers
agree. The record makes no claim about other repositories.
Develop
| Command | Job |
|---|---|
bun test | Runs the unit tests. |
bun run typecheck | Runs the TypeScript type check. |
bun run lint | Runs the lint and format check. |
bun run build | Writes the Node bundle dist/cli.js. |
bun run compile | Writes the compiled executable for this platform into bin/. |
bun run smoke -- <command...> | Runs the smoke test against one way to run the tool. |
bun run privacy | Runs the privacy check on the tracked files and the commit messages. |
bun run hooks | Installs the commit-msg hook that runs the privacy check on each message. |
bun run audit:ledger | Reverts each fix of docs/fix-ledger-2.0.0.md in a scratch copy and checks that a test of that fix fails. |
bun run compare:docs | Compares two docs scan reports, writes a parity row, or gives a cause to each unparsed file. |
bun run check:design | Finds each reference to a section of docs/design.md that has no heading with that number. |
Documentation
| Document | Contents |
|---|---|
| docs/architecture/OVERVIEW.md | The project, its metrics and its layout. |
| docs/architecture/ARCHITECTURE.md | The layers, the key decisions, the build and the test strategy. |
| docs/architecture/COMPONENTS.md | Each module, with the real signatures of its exports. |
| docs/architecture/DATAFLOW.md | One run of each subcommand, step by step. |
| docs/architecture/API.md | The commands, the flags, the config keys, the exit codes and the report files. |
| docs/architecture/FILE_INVENTORY.md | Every source file with its area and disposition. Generated. |
| docs/architecture/TEST_COVERAGE.md | The source files that a test imports. Generated. |
| docs/architecture/DEPENDENCY_GRAPH.md | Who imports whom. Generated. |
| docs/architecture/unused-analysis.md | Files and exports with no importer. Generated. |
| docs/architecture/duplicate-symbols.md | Names that more than one file defines. Generated. |
| docs/design.md | The design of each subcommand, section by section. |
Run repo-tools check . --docs=docs/architecture to compare the numbers in these documents with the code.
Design
docs/design.md describes the modules, the output rules, the config, the extension contract,
the privacy check and the verification. Its section "The 2.0.0 engine" describes map.
License
MIT. See LICENSE.
Third-party notices: NOTICE holds the licence of mintlify/writer (MIT), from which parts of
repo-tools docs derive.