tld analyze
View Markdowntld analyze reads your source code and generates architecture diagrams from it. It’s a one-shot command: scan, discover, write.
tld analyze .How it works
Section titled “How it works”Under the hood, tld analyze runs a pipeline:
-
Parse: Tree-sitter grammars parse every source file into abstract syntax trees. Each supported language has its own grammar, so the parsing is language-aware, not regex-based.
-
Extract: The parser extracts named symbols , functions, classes, structs, interfaces, modules. It also extracts references , imports, function calls, method invocations, type annotations.
-
Filter: Noise filters remove test files, generated code, and boilerplate that would clutter the diagram.
-
Cluster: Components are grouped by heuristics. Related files in the same package become a single element. Dependencies between packages become connectors.
-
Threshold: Density controls prevent “hairball” diagrams. If a utility is imported by 50 files, it gets collapsed into a group rather than generating 50 connector lines.
-
Write: The results go into your workspace YAML files (
elements.yaml,connectors.yaml).
Supported languages
Section titled “Supported languages”| Language | Extensions | Tree-sitter grammar |
|---|---|---|
| Go | .go | tree-sitter-go |
| TypeScript / JavaScript | .ts, .tsx, .js, .jsx | tree-sitter-typescript |
| Python | .py | tree-sitter-python |
| Java | .java | tree-sitter-java |
| C++ | .cpp, .hpp, .cc, .h | tree-sitter-cpp |
| Rust | .rs | tree-sitter-rust |
Open an issue or even better, open a PR :). Tree-sitter supports 150+ languages, it is relatively easy to add new ones but might take time to polish.
Basic usage
Section titled “Basic usage”# Scan everythingtld analyze .
# Scan a specific directorytld analyze ./src
# Scan only Go filestld analyze . --language go
# Multiple languagestld analyze . --language go --language pythonDensity control
Section titled “Density control”Dense codebases produce dense diagrams. These flags control how much detail you get:
tld analyze . \ --max-elements-per-view 30 \ --max-connectors-per-view 60 \ --max-incoming-per-element 10 \ --max-outgoing-per-element 10| Flag | Default | What it does |
|---|---|---|
--max-elements-per-view | 50 | Cap on elements in a single view |
--max-incoming-per-element | 15 | Collapses connections if an element receives too many |
--max-outgoing-per-element | 15 | Collapses connections if an element sends too many |
--max-expanded-connectors-per-group | 30 | Merges file-level connectors into folder-level ones |
The defaults are reasonable for most projects. If output diagrams look sparse, raise the limits. If they’re noisy, lower them.
How collapsing works
Section titled “How collapsing works”When an element exceeds a threshold, the connectors are collapsed into a group connector. Instead of 50 individual lines to a utility file, you get one group connector saying “50 connections to utils/”. The tooltip shows the details. The diagram stays clean.
CI/CD automation
Section titled “CI/CD automation”Dry run
Section titled “Dry run”Preview changes without modifying files:
tld analyze . --dry-runShows what would be added, updated, or removed. Nothing is written.
Fail on drift
Section titled “Fail on drift”Detect when code and diagrams have diverged:
tld analyze . --dry-run --fail-on-driftReturns exit code 1 if the code has elements or connections not yet reflected in the diagrams. Designed to be suitable for CI, fail the build if architecture docs are stale.
JSON output
Section titled “JSON output”Machine-readable results:
tld analyze . --format json{ "elements_found": 42, "connectors_found": 89, "drift": [ {"type": "new_element", "ref": "payment-svc", "reason": "found in source but not in YAML"} ]}Common CI commands
Section titled “Common CI commands”# Validate diagrams match codetld analyze ./src --dry-run --fail-on-drift
# Specific language + density limits for cleaner outputtld analyze ./pkg --language go --max-elements-per-view 50
# Machine-readable for custom reportingtld analyze . --format jsonAdditive by design
Section titled “Additive by design”tld analyze is non-destructive. It adds new elements and updates existing ones, but never removes things you’ve manually documented. If you hand-crafted a beautiful diagram and the code scan finds additional services, it adds them alongside your work.
This is by design. Static analysis can’t understand intent. It finds what’s in the code, but you know what’s in the architecture. The two should complement each other.
When code and diagrams disagree
Section titled “When code and diagrams disagree”Sometimes the scanner finds things that don’t match your mental model. Maybe it grouped things differently than you would. Maybe it found a dependency that doesn’t actually exist at the architecture level.
When to use analyze vs watch
Section titled “When to use analyze vs watch”tld analyze is one-shot.
Ideal for:
- Initial diagram from an existing codebase
- Periodic sync before reviews
- CI checks for drift
tld watch is continuous. It stays running, monitoring changes. Ideal for:
- Seeing live impact of code changes on architecture
- Keeping diagrams in sync during active development
They use the same scanning engine under the hood.
Embedding support (optional)
Section titled “Embedding support (optional)”For semantic grouping beyond folder structure:
tld analyze . --embedding-provider openai --embedding-model text-embedding-3-smallWith embeddings, related components cluster together based on what they do, not just where they live. A validation.go file and a validator.py file might share nothing structurally but serve the same purpose, embeddings allow tld to catch this. Also helps with refactoring and renaming symbols.
Requires an OpenAI compatible endpoint. Configure via global config or environment variables.