treemapper 1.6.1__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- treemapper-2.2.0/.gitignore +53 -0
- treemapper-2.2.0/CHANGELOG.md +64 -0
- treemapper-2.2.0/PKG-INFO +120 -0
- treemapper-2.2.0/README.md +76 -0
- treemapper-2.2.0/pyproject.toml +107 -0
- treemapper-2.2.0/src/treemapper/__init__.py +24 -0
- treemapper-2.2.0/src/treemapper/__main__.py +6 -0
- treemapper-2.2.0/src/treemapper/cli.py +13 -0
- treemapper-2.2.0/src/treemapper/mcp_main.py +11 -0
- treemapper-2.2.0/src/treemapper/version.py +1 -0
- treemapper-2.2.0/tests/conftest.py +59 -0
- treemapper-2.2.0/tests/test_api.py +30 -0
- treemapper-2.2.0/tests/test_cli.py +82 -0
- treemapper-1.6.1/CHANGELOG.md +0 -59
- treemapper-1.6.1/PKG-INFO +0 -329
- treemapper-1.6.1/README.md +0 -253
- treemapper-1.6.1/diffctx/Cargo.lock +0 -1651
- treemapper-1.6.1/diffctx/Cargo.toml +0 -174
- treemapper-1.6.1/diffctx/src/analytics.rs +0 -791
- treemapper-1.6.1/diffctx/src/candidate_files.rs +0 -86
- treemapper-1.6.1/diffctx/src/config/analytics.rs +0 -17
- treemapper-1.6.1/diffctx/src/config/bm25.rs +0 -21
- treemapper-1.6.1/diffctx/src/config/budget.rs +0 -21
- treemapper-1.6.1/diffctx/src/config/category_weights.rs +0 -117
- treemapper-1.6.1/diffctx/src/config/edge_weights.rs +0 -246
- treemapper-1.6.1/diffctx/src/config/env_overrides.rs +0 -125
- treemapper-1.6.1/diffctx/src/config/extensions.rs +0 -134
- treemapper-1.6.1/diffctx/src/config/filtering.rs +0 -38
- treemapper-1.6.1/diffctx/src/config/fragmentation.rs +0 -17
- treemapper-1.6.1/diffctx/src/config/git.rs +0 -23
- treemapper-1.6.1/diffctx/src/config/graph_filtering.rs +0 -23
- treemapper-1.6.1/diffctx/src/config/importance.rs +0 -93
- treemapper-1.6.1/diffctx/src/config/limits.rs +0 -152
- treemapper-1.6.1/diffctx/src/config/mod.rs +0 -21
- treemapper-1.6.1/diffctx/src/config/mode.rs +0 -45
- treemapper-1.6.1/diffctx/src/config/needs.rs +0 -63
- treemapper-1.6.1/diffctx/src/config/parsers.rs +0 -27
- treemapper-1.6.1/diffctx/src/config/render.rs +0 -15
- treemapper-1.6.1/diffctx/src/config/scoring.rs +0 -21
- treemapper-1.6.1/diffctx/src/config/selection.rs +0 -81
- treemapper-1.6.1/diffctx/src/config/tokenization.rs +0 -19
- treemapper-1.6.1/diffctx/src/config/weights.rs +0 -364
- treemapper-1.6.1/diffctx/src/core.rs +0 -209
- treemapper-1.6.1/diffctx/src/discovery.rs +0 -320
- treemapper-1.6.1/diffctx/src/edges/base.rs +0 -294
- treemapper-1.6.1/diffctx/src/edges/config_edges/build_system.rs +0 -171
- treemapper-1.6.1/diffctx/src/edges/config_edges/cicd.rs +0 -323
- treemapper-1.6.1/diffctx/src/edges/config_edges/docker.rs +0 -266
- treemapper-1.6.1/diffctx/src/edges/config_edges/generic.rs +0 -290
- treemapper-1.6.1/diffctx/src/edges/config_edges/helm.rs +0 -342
- treemapper-1.6.1/diffctx/src/edges/config_edges/kubernetes.rs +0 -517
- treemapper-1.6.1/diffctx/src/edges/config_edges/mod.rs +0 -19
- treemapper-1.6.1/diffctx/src/edges/document/mod.rs +0 -153
- treemapper-1.6.1/diffctx/src/edges/history/cochange.rs +0 -132
- treemapper-1.6.1/diffctx/src/edges/history/mod.rs +0 -7
- treemapper-1.6.1/diffctx/src/edges/mod.rs +0 -137
- treemapper-1.6.1/diffctx/src/edges/semantic/ansible.rs +0 -206
- treemapper-1.6.1/diffctx/src/edges/semantic/bazel.rs +0 -268
- treemapper-1.6.1/diffctx/src/edges/semantic/c_family.rs +0 -387
- treemapper-1.6.1/diffctx/src/edges/semantic/cargo_edges.rs +0 -237
- treemapper-1.6.1/diffctx/src/edges/semantic/clojure.rs +0 -181
- treemapper-1.6.1/diffctx/src/edges/semantic/css.rs +0 -73
- treemapper-1.6.1/diffctx/src/edges/semantic/dart.rs +0 -278
- treemapper-1.6.1/diffctx/src/edges/semantic/dbt.rs +0 -146
- treemapper-1.6.1/diffctx/src/edges/semantic/dotnet.rs +0 -380
- treemapper-1.6.1/diffctx/src/edges/semantic/elixir.rs +0 -165
- treemapper-1.6.1/diffctx/src/edges/semantic/erlang.rs +0 -154
- treemapper-1.6.1/diffctx/src/edges/semantic/go.rs +0 -317
- treemapper-1.6.1/diffctx/src/edges/semantic/graphql.rs +0 -146
- treemapper-1.6.1/diffctx/src/edges/semantic/haskell.rs +0 -291
- treemapper-1.6.1/diffctx/src/edges/semantic/javascript.rs +0 -420
- treemapper-1.6.1/diffctx/src/edges/semantic/julia.rs +0 -156
- treemapper-1.6.1/diffctx/src/edges/semantic/jvm.rs +0 -420
- treemapper-1.6.1/diffctx/src/edges/semantic/latex.rs +0 -116
- treemapper-1.6.1/diffctx/src/edges/semantic/lua.rs +0 -140
- treemapper-1.6.1/diffctx/src/edges/semantic/mod.rs +0 -81
- treemapper-1.6.1/diffctx/src/edges/semantic/nim.rs +0 -196
- treemapper-1.6.1/diffctx/src/edges/semantic/nix.rs +0 -81
- treemapper-1.6.1/diffctx/src/edges/semantic/ocaml.rs +0 -141
- treemapper-1.6.1/diffctx/src/edges/semantic/openapi.rs +0 -151
- treemapper-1.6.1/diffctx/src/edges/semantic/perl.rs +0 -207
- treemapper-1.6.1/diffctx/src/edges/semantic/php.rs +0 -166
- treemapper-1.6.1/diffctx/src/edges/semantic/prisma.rs +0 -132
- treemapper-1.6.1/diffctx/src/edges/semantic/protobuf.rs +0 -126
- treemapper-1.6.1/diffctx/src/edges/semantic/python.rs +0 -357
- treemapper-1.6.1/diffctx/src/edges/semantic/r_lang.rs +0 -173
- treemapper-1.6.1/diffctx/src/edges/semantic/ruby.rs +0 -140
- treemapper-1.6.1/diffctx/src/edges/semantic/rust_lang.rs +0 -491
- treemapper-1.6.1/diffctx/src/edges/semantic/shell.rs +0 -125
- treemapper-1.6.1/diffctx/src/edges/semantic/sql.rs +0 -133
- treemapper-1.6.1/diffctx/src/edges/semantic/swift.rs +0 -150
- treemapper-1.6.1/diffctx/src/edges/semantic/tags.rs +0 -65
- treemapper-1.6.1/diffctx/src/edges/semantic/terraform.rs +0 -597
- treemapper-1.6.1/diffctx/src/edges/semantic/zig.rs +0 -165
- treemapper-1.6.1/diffctx/src/edges/similarity/lexical.rs +0 -264
- treemapper-1.6.1/diffctx/src/edges/similarity/mod.rs +0 -7
- treemapper-1.6.1/diffctx/src/edges/structural/containment.rs +0 -62
- treemapper-1.6.1/diffctx/src/edges/structural/mod.rs +0 -13
- treemapper-1.6.1/diffctx/src/edges/structural/sibling.rs +0 -89
- treemapper-1.6.1/diffctx/src/edges/structural/testing.rs +0 -266
- treemapper-1.6.1/diffctx/src/filtering.rs +0 -304
- treemapper-1.6.1/diffctx/src/fragmentation.rs +0 -350
- treemapper-1.6.1/diffctx/src/git.rs +0 -595
- treemapper-1.6.1/diffctx/src/graph.rs +0 -708
- treemapper-1.6.1/diffctx/src/graph_export.rs +0 -545
- treemapper-1.6.1/diffctx/src/interval.rs +0 -76
- treemapper-1.6.1/diffctx/src/languages.rs +0 -335
- treemapper-1.6.1/diffctx/src/lib.rs +0 -31
- treemapper-1.6.1/diffctx/src/main.rs +0 -87
- treemapper-1.6.1/diffctx/src/memory_pipeline.rs +0 -330
- treemapper-1.6.1/diffctx/src/mode.rs +0 -96
- treemapper-1.6.1/diffctx/src/parsers/config_parser.rs +0 -193
- treemapper-1.6.1/diffctx/src/parsers/generic.rs +0 -53
- treemapper-1.6.1/diffctx/src/parsers/markdown.rs +0 -105
- treemapper-1.6.1/diffctx/src/parsers/mod.rs +0 -142
- treemapper-1.6.1/diffctx/src/parsers/tree_sitter_strategy.rs +0 -1702
- treemapper-1.6.1/diffctx/src/pipeline.rs +0 -601
- treemapper-1.6.1/diffctx/src/postpass.rs +0 -265
- treemapper-1.6.1/diffctx/src/ppr.rs +0 -608
- treemapper-1.6.1/diffctx/src/project_graph.rs +0 -235
- treemapper-1.6.1/diffctx/src/pybridge.rs +0 -727
- treemapper-1.6.1/diffctx/src/render.rs +0 -237
- treemapper-1.6.1/diffctx/src/scoring.rs +0 -273
- treemapper-1.6.1/diffctx/src/select.rs +0 -683
- treemapper-1.6.1/diffctx/src/signatures.rs +0 -119
- treemapper-1.6.1/diffctx/src/stopwords.rs +0 -990
- treemapper-1.6.1/diffctx/src/test_harness.rs +0 -384
- treemapper-1.6.1/diffctx/src/tokenizer.rs +0 -34
- treemapper-1.6.1/diffctx/src/types.rs +0 -283
- treemapper-1.6.1/diffctx/src/utility/boltzmann.rs +0 -253
- treemapper-1.6.1/diffctx/src/utility/importance.rs +0 -142
- treemapper-1.6.1/diffctx/src/utility/mod.rs +0 -9
- treemapper-1.6.1/diffctx/src/utility/needs.rs +0 -700
- treemapper-1.6.1/diffctx/src/utility/scoring.rs +0 -641
- treemapper-1.6.1/diffctx/tests/common/mod.rs +0 -274
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_api.py +0 -31
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_constants.py +0 -11
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_handlers.py +0 -27
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_models.py +0 -25
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_module.js +0 -23
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_services.py +0 -37
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_types.py +0 -31
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_unrelated.yaml +0 -8
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_utils.py +0 -31
- treemapper-1.6.1/diffctx/tests/fixtures/garbage/garbage_validators.py +0 -23
- treemapper-1.6.1/diffctx/tests/yaml_cases.rs +0 -279
- treemapper-1.6.1/pyproject.toml +0 -299
- treemapper-1.6.1/rust-toolchain.toml +0 -4
- treemapper-1.6.1/src/treemapper/__init__.py +0 -98
- treemapper-1.6.1/src/treemapper/__main__.py +0 -17
- treemapper-1.6.1/src/treemapper/cli.py +0 -468
- treemapper-1.6.1/src/treemapper/clipboard.py +0 -81
- treemapper-1.6.1/src/treemapper/diffctx/__init__.py +0 -7
- treemapper-1.6.1/src/treemapper/diffctx/graph_analytics.py +0 -59
- treemapper-1.6.1/src/treemapper/diffctx/graph_export.py +0 -46
- treemapper-1.6.1/src/treemapper/diffctx/pipeline.py +0 -108
- treemapper-1.6.1/src/treemapper/diffctx/project_graph.py +0 -27
- treemapper-1.6.1/src/treemapper/ignore.py +0 -345
- treemapper-1.6.1/src/treemapper/logger.py +0 -32
- treemapper-1.6.1/src/treemapper/mcp/README.md +0 -104
- treemapper-1.6.1/src/treemapper/mcp/__main__.py +0 -4
- treemapper-1.6.1/src/treemapper/mcp/formatting.py +0 -9
- treemapper-1.6.1/src/treemapper/mcp/security.py +0 -30
- treemapper-1.6.1/src/treemapper/mcp/server.py +0 -237
- treemapper-1.6.1/src/treemapper/py.typed +0 -0
- treemapper-1.6.1/src/treemapper/tokens.py +0 -49
- treemapper-1.6.1/src/treemapper/tree.py +0 -279
- treemapper-1.6.1/src/treemapper/treemapper.py +0 -292
- treemapper-1.6.1/src/treemapper/version.py +0 -1
- treemapper-1.6.1/src/treemapper/writer.py +0 -400
- {treemapper-1.6.1 → treemapper-2.2.0}/LICENSE +0 -0
- {treemapper-1.6.1/src/treemapper/mcp → treemapper-2.2.0/tests}/__init__.py +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# Default output files
|
|
30
|
+
tree.yaml
|
|
31
|
+
tree.json
|
|
32
|
+
tree.md
|
|
33
|
+
tree.txt
|
|
34
|
+
|
|
35
|
+
# Virtual environments
|
|
36
|
+
.venv/
|
|
37
|
+
venv/
|
|
38
|
+
ENV/
|
|
39
|
+
|
|
40
|
+
# Tooling caches
|
|
41
|
+
.pytest_cache/
|
|
42
|
+
.mypy_cache/
|
|
43
|
+
.ruff_cache/
|
|
44
|
+
.hypothesis/
|
|
45
|
+
|
|
46
|
+
# IDE / OS
|
|
47
|
+
.idea/
|
|
48
|
+
.vscode/
|
|
49
|
+
.DS_Store
|
|
50
|
+
|
|
51
|
+
# Local-only planning
|
|
52
|
+
TODO.md
|
|
53
|
+
test-repos
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.2.0] - 2026-06-20
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Bumped the dependency floor to `diffctx>=1.10.0,<2.0`. This pulls in the
|
|
15
|
+
diffctx 1.10 engine: the calibrated default `--tau` of 0.12 (tighter
|
|
16
|
+
diff-context selection by default), the 256 KB MCP file cap, and the
|
|
17
|
+
document/import edge correctness fixes. No treemapper API changes.
|
|
18
|
+
|
|
19
|
+
## [2.1.0] - 2026-06-14
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Bumped the dependency floor to `diffctx>=1.9.1,<2.0`. This pulls in the diffctx
|
|
24
|
+
1.9 diff-context improvements for `treemapper . --diff`: an orientation header
|
|
25
|
+
(`commit_message` + `changed_files`) at the top of every format, a `role:
|
|
26
|
+
changed` marker on fragments overlapping the diff with changed code emitted
|
|
27
|
+
first, context ordered by descending per-file relevance, and merging of
|
|
28
|
+
line-contiguous same-role fragments. The `>=1.9.1` floor also remains a hard
|
|
29
|
+
requirement for the branded `run(prog=…, version=…)` entry — the pre-1.8
|
|
30
|
+
fallback path was removed, so there is no unbranded code path.
|
|
31
|
+
- `treemapper` drives the engine through the public
|
|
32
|
+
`diffctx.run(prog="treemapper", version=…)` entry, so `--help`, `--version`,
|
|
33
|
+
and error prefixes are branded as `treemapper`. The `treemapper-mcp` install
|
|
34
|
+
hint is likewise branded via the engine's `prog`/`extra` parameters.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- GitHub Actions release workflow (`cd.yml`) that builds the pure wheel + sdist
|
|
39
|
+
and publishes to PyPI via OIDC trusted publishing.
|
|
40
|
+
|
|
41
|
+
## [2.0.0]
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- **TreeMapper is now a thin wrapper over the [`diffctx`](https://pypi.org/project/diffctx/)
|
|
46
|
+
engine.** All directory traversal, serialization, and git-diff context
|
|
47
|
+
selection are delegated to `diffctx` (pinned `>=1.7,<2.0`). TreeMapper itself
|
|
48
|
+
ships only the `treemapper` command, the `treemapper-mcp` server entry point,
|
|
49
|
+
and a Python API that re-exports `map_directory`, `build_diff_context`, and
|
|
50
|
+
the `to_yaml`/`to_json`/`to_text`/`to_markdown` serializers. No engine logic
|
|
51
|
+
is duplicated.
|
|
52
|
+
- Pure-Python wheel (built with hatchling). The native engine arrives through
|
|
53
|
+
the `diffctx` dependency.
|
|
54
|
+
|
|
55
|
+
### Note
|
|
56
|
+
|
|
57
|
+
Releases `1.0.0` through `1.6.1` shipped TreeMapper as a self-contained package;
|
|
58
|
+
that lineage was renamed to `diffctx` at `diffctx` `1.7.0`. TreeMapper `2.0.0`
|
|
59
|
+
re-establishes the `treemapper` name as the product layer on top of that engine.
|
|
60
|
+
|
|
61
|
+
[Unreleased]: https://github.com/nikolay-e/treemapper/compare/v2.2.0...HEAD
|
|
62
|
+
[2.2.0]: https://github.com/nikolay-e/treemapper/compare/v2.1.0...v2.2.0
|
|
63
|
+
[2.1.0]: https://github.com/nikolay-e/treemapper/compare/v2.0.0...v2.1.0
|
|
64
|
+
[2.0.0]: https://github.com/nikolay-e/treemapper/releases/tag/v2.0.0
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: treemapper
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: Map a codebase to YAML/JSON/Markdown/text and select smart git-diff context for LLMs
|
|
5
|
+
Project-URL: Changelog, https://github.com/nikolay-e/treemapper/releases
|
|
6
|
+
Project-URL: Homepage, https://github.com/nikolay-e/treemapper
|
|
7
|
+
Project-URL: Issues, https://github.com/nikolay-e/treemapper/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/nikolay-e/treemapper
|
|
9
|
+
Author-email: Nikolay Eremeev <nikolay.eremeev@outlook.com>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,claude,code-context,codebase,context-selection,diff-context,directory-tree,git-diff,json,llm,mcp,tree,yaml
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Software Development
|
|
24
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Requires-Dist: diffctx<2.0,>=1.10.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: mypy<3.0,>=2.1.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pre-commit<5.0,>=3.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-timeout<3.0,>=2.4.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest<10.0,>=7.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pyyaml<8.0,>=6.0.2; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff<1.0,>=0.4; extra == 'dev'
|
|
36
|
+
Requires-Dist: types-pyyaml<7.0,>=6.0.12.20260518; extra == 'dev'
|
|
37
|
+
Provides-Extra: full
|
|
38
|
+
Requires-Dist: diffctx[full]<2.0,>=1.10.0; extra == 'full'
|
|
39
|
+
Provides-Extra: mcp
|
|
40
|
+
Requires-Dist: diffctx[mcp]<2.0,>=1.10.0; extra == 'mcp'
|
|
41
|
+
Provides-Extra: tree-sitter
|
|
42
|
+
Requires-Dist: diffctx[tree-sitter]<2.0,>=1.10.0; extra == 'tree-sitter'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# TreeMapper
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/treemapper/)
|
|
48
|
+
[](https://github.com/nikolay-e/treemapper/blob/main/LICENSE)
|
|
49
|
+
|
|
50
|
+
**Map a codebase into LLM-ready context.** TreeMapper serializes an entire
|
|
51
|
+
directory tree — structure plus file contents — to YAML, JSON, Markdown, or
|
|
52
|
+
text, and selects the minimal smart git-diff context needed to understand a
|
|
53
|
+
change. Paste the output into Claude, ChatGPT, or any LLM.
|
|
54
|
+
|
|
55
|
+
TreeMapper is a thin command-line product built on the
|
|
56
|
+
[`diffctx`](https://pypi.org/project/diffctx/) engine. All traversal,
|
|
57
|
+
serialization, and diff-context selection live in `diffctx`; TreeMapper
|
|
58
|
+
re-exposes them under the `treemapper` name so there is a single source of
|
|
59
|
+
truth and no duplicated logic.
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pipx install treemapper # recommended: isolated, no venv needed
|
|
65
|
+
pip install treemapper # or with pip
|
|
66
|
+
pip install 'treemapper[tree-sitter]' # + AST parsing for smarter diff context
|
|
67
|
+
pip install 'treemapper[mcp]' # + MCP server for AI assistants
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
treemapper . # map current directory to YAML (stdout)
|
|
74
|
+
treemapper /path/to/project # map a specific directory
|
|
75
|
+
treemapper . -f json # output as JSON
|
|
76
|
+
treemapper . -f md --save # save as tree.md
|
|
77
|
+
treemapper . --no-content # structure only, no file contents
|
|
78
|
+
treemapper . -c # copy output to clipboard
|
|
79
|
+
treemapper . --diff HEAD~1 # smart context for the last commit
|
|
80
|
+
treemapper . --diff main..HEAD # smart context for a branch range
|
|
81
|
+
treemapper graph . # project dependency graph (mermaid)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Two modes
|
|
85
|
+
|
|
86
|
+
- **Tree mapping** (`treemapper .`) — walks the directory tree, respects
|
|
87
|
+
hierarchical ignore patterns (`.gitignore`, `.diffctx/ignore`), reads file
|
|
88
|
+
contents with binary/encoding detection, and serializes the result.
|
|
89
|
+
- **Diff context** (`treemapper . --diff`) — analyzes a git diff and selects
|
|
90
|
+
the minimal set of code fragments needed to understand the change, instead
|
|
91
|
+
of dumping whole files.
|
|
92
|
+
|
|
93
|
+
Run `treemapper --help` for the full flag reference.
|
|
94
|
+
|
|
95
|
+
## Python API
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
import treemapper
|
|
99
|
+
|
|
100
|
+
tree = treemapper.map_directory(".", no_content=False)
|
|
101
|
+
print(treemapper.to_yaml(tree))
|
|
102
|
+
|
|
103
|
+
context = treemapper.build_diff_context(root_dir=".", diff_range="HEAD~1")
|
|
104
|
+
|
|
105
|
+
# treemapper.run drives the full branded CLI from Python (same surface as the command)
|
|
106
|
+
treemapper.run(["graph", "."], prog="treemapper", version=treemapper.__version__)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Every run reports an **exact `tiktoken` token count** (not a `chars / 4`
|
|
110
|
+
estimate), so you know the real context cost before you paste.
|
|
111
|
+
|
|
112
|
+
## Relationship to diffctx
|
|
113
|
+
|
|
114
|
+
TreeMapper is the user-facing distribution; `diffctx` is the reusable engine.
|
|
115
|
+
Pin compatibility is `diffctx>=1.10.0,<2.0`. If you are embedding the engine in
|
|
116
|
+
your own tool, depend on `diffctx` directly.
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
Apache 2.0
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# TreeMapper
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/treemapper/)
|
|
4
|
+
[](https://github.com/nikolay-e/treemapper/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
**Map a codebase into LLM-ready context.** TreeMapper serializes an entire
|
|
7
|
+
directory tree — structure plus file contents — to YAML, JSON, Markdown, or
|
|
8
|
+
text, and selects the minimal smart git-diff context needed to understand a
|
|
9
|
+
change. Paste the output into Claude, ChatGPT, or any LLM.
|
|
10
|
+
|
|
11
|
+
TreeMapper is a thin command-line product built on the
|
|
12
|
+
[`diffctx`](https://pypi.org/project/diffctx/) engine. All traversal,
|
|
13
|
+
serialization, and diff-context selection live in `diffctx`; TreeMapper
|
|
14
|
+
re-exposes them under the `treemapper` name so there is a single source of
|
|
15
|
+
truth and no duplicated logic.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pipx install treemapper # recommended: isolated, no venv needed
|
|
21
|
+
pip install treemapper # or with pip
|
|
22
|
+
pip install 'treemapper[tree-sitter]' # + AST parsing for smarter diff context
|
|
23
|
+
pip install 'treemapper[mcp]' # + MCP server for AI assistants
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
treemapper . # map current directory to YAML (stdout)
|
|
30
|
+
treemapper /path/to/project # map a specific directory
|
|
31
|
+
treemapper . -f json # output as JSON
|
|
32
|
+
treemapper . -f md --save # save as tree.md
|
|
33
|
+
treemapper . --no-content # structure only, no file contents
|
|
34
|
+
treemapper . -c # copy output to clipboard
|
|
35
|
+
treemapper . --diff HEAD~1 # smart context for the last commit
|
|
36
|
+
treemapper . --diff main..HEAD # smart context for a branch range
|
|
37
|
+
treemapper graph . # project dependency graph (mermaid)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Two modes
|
|
41
|
+
|
|
42
|
+
- **Tree mapping** (`treemapper .`) — walks the directory tree, respects
|
|
43
|
+
hierarchical ignore patterns (`.gitignore`, `.diffctx/ignore`), reads file
|
|
44
|
+
contents with binary/encoding detection, and serializes the result.
|
|
45
|
+
- **Diff context** (`treemapper . --diff`) — analyzes a git diff and selects
|
|
46
|
+
the minimal set of code fragments needed to understand the change, instead
|
|
47
|
+
of dumping whole files.
|
|
48
|
+
|
|
49
|
+
Run `treemapper --help` for the full flag reference.
|
|
50
|
+
|
|
51
|
+
## Python API
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import treemapper
|
|
55
|
+
|
|
56
|
+
tree = treemapper.map_directory(".", no_content=False)
|
|
57
|
+
print(treemapper.to_yaml(tree))
|
|
58
|
+
|
|
59
|
+
context = treemapper.build_diff_context(root_dir=".", diff_range="HEAD~1")
|
|
60
|
+
|
|
61
|
+
# treemapper.run drives the full branded CLI from Python (same surface as the command)
|
|
62
|
+
treemapper.run(["graph", "."], prog="treemapper", version=treemapper.__version__)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Every run reports an **exact `tiktoken` token count** (not a `chars / 4`
|
|
66
|
+
estimate), so you know the real context cost before you paste.
|
|
67
|
+
|
|
68
|
+
## Relationship to diffctx
|
|
69
|
+
|
|
70
|
+
TreeMapper is the user-facing distribution; `diffctx` is the reusable engine.
|
|
71
|
+
Pin compatibility is `diffctx>=1.10.0,<2.0`. If you are embedding the engine in
|
|
72
|
+
your own tool, depend on `diffctx` directly.
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
Apache 2.0
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "hatchling.build"
|
|
3
|
+
requires = [ "hatchling>=1.25,<2.0" ]
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "treemapper"
|
|
7
|
+
dynamic = [ "version" ]
|
|
8
|
+
description = "Map a codebase to YAML/JSON/Markdown/text and select smart git-diff context for LLMs"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
keywords = [
|
|
11
|
+
"ai",
|
|
12
|
+
"claude",
|
|
13
|
+
"code-context",
|
|
14
|
+
"codebase",
|
|
15
|
+
"context-selection",
|
|
16
|
+
"diff-context",
|
|
17
|
+
"directory-tree",
|
|
18
|
+
"git-diff",
|
|
19
|
+
"json",
|
|
20
|
+
"llm",
|
|
21
|
+
"mcp",
|
|
22
|
+
"tree",
|
|
23
|
+
"yaml",
|
|
24
|
+
]
|
|
25
|
+
license = "Apache-2.0"
|
|
26
|
+
authors = [
|
|
27
|
+
{ name = "Nikolay Eremeev", email = "nikolay.eremeev@outlook.com" },
|
|
28
|
+
]
|
|
29
|
+
requires-python = ">=3.10"
|
|
30
|
+
classifiers = [
|
|
31
|
+
"Development Status :: 5 - Production/Stable",
|
|
32
|
+
"Environment :: Console",
|
|
33
|
+
"Intended Audience :: Developers",
|
|
34
|
+
"Operating System :: OS Independent",
|
|
35
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
36
|
+
"Programming Language :: Python :: 3.10",
|
|
37
|
+
"Programming Language :: Python :: 3.11",
|
|
38
|
+
"Programming Language :: Python :: 3.12",
|
|
39
|
+
"Programming Language :: Python :: 3.13",
|
|
40
|
+
"Programming Language :: Python :: 3.14",
|
|
41
|
+
"Topic :: Software Development",
|
|
42
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
43
|
+
"Topic :: Utilities",
|
|
44
|
+
"Typing :: Typed",
|
|
45
|
+
]
|
|
46
|
+
dependencies = [
|
|
47
|
+
"diffctx>=1.10.0,<2.0",
|
|
48
|
+
]
|
|
49
|
+
optional-dependencies.dev = [
|
|
50
|
+
"mypy>=2.1.0,<3.0",
|
|
51
|
+
"pre-commit>=3.0,<5.0",
|
|
52
|
+
"pytest>=7.0,<10.0",
|
|
53
|
+
"pytest-timeout>=2.4.0,<3.0",
|
|
54
|
+
"pyyaml>=6.0.2,<8.0",
|
|
55
|
+
"ruff>=0.4,<1.0",
|
|
56
|
+
"types-pyyaml>=6.0.12.20260518,<7.0",
|
|
57
|
+
]
|
|
58
|
+
optional-dependencies.full = [
|
|
59
|
+
"diffctx[full]>=1.10.0,<2.0",
|
|
60
|
+
]
|
|
61
|
+
optional-dependencies.mcp = [
|
|
62
|
+
"diffctx[mcp]>=1.10.0,<2.0",
|
|
63
|
+
]
|
|
64
|
+
optional-dependencies.tree-sitter = [
|
|
65
|
+
"diffctx[tree-sitter]>=1.10.0,<2.0",
|
|
66
|
+
]
|
|
67
|
+
urls.Changelog = "https://github.com/nikolay-e/treemapper/releases"
|
|
68
|
+
urls.Homepage = "https://github.com/nikolay-e/treemapper"
|
|
69
|
+
urls.Issues = "https://github.com/nikolay-e/treemapper/issues"
|
|
70
|
+
urls.Repository = "https://github.com/nikolay-e/treemapper"
|
|
71
|
+
scripts.treemapper = "treemapper.cli:main"
|
|
72
|
+
scripts.treemapper-mcp = "treemapper.mcp_main:main"
|
|
73
|
+
|
|
74
|
+
[tool.hatch.version]
|
|
75
|
+
path = "src/treemapper/version.py"
|
|
76
|
+
|
|
77
|
+
[tool.hatch.build.targets.wheel]
|
|
78
|
+
packages = [ "src/treemapper" ]
|
|
79
|
+
|
|
80
|
+
[tool.hatch.build.targets.sdist]
|
|
81
|
+
include = [
|
|
82
|
+
"src/treemapper",
|
|
83
|
+
"tests",
|
|
84
|
+
"README.md",
|
|
85
|
+
"CHANGELOG.md",
|
|
86
|
+
"LICENSE",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.ruff]
|
|
90
|
+
target-version = "py310"
|
|
91
|
+
line-length = 130
|
|
92
|
+
lint.select = [ "C901", "E", "F", "I", "N", "RUF", "UP", "W" ]
|
|
93
|
+
lint.ignore = [ "E501" ]
|
|
94
|
+
|
|
95
|
+
[tool.pytest.ini_options]
|
|
96
|
+
testpaths = [ "tests" ]
|
|
97
|
+
pythonpath = [ "src" ]
|
|
98
|
+
timeout = 30
|
|
99
|
+
|
|
100
|
+
[tool.mypy]
|
|
101
|
+
python_version = "3.10"
|
|
102
|
+
strict = true
|
|
103
|
+
files = [ "src" ]
|
|
104
|
+
|
|
105
|
+
[[tool.mypy.overrides]]
|
|
106
|
+
module = [ "diffctx.*" ]
|
|
107
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from diffctx import (
|
|
4
|
+
build_diff_context,
|
|
5
|
+
map_directory,
|
|
6
|
+
run,
|
|
7
|
+
to_json,
|
|
8
|
+
to_markdown,
|
|
9
|
+
to_text,
|
|
10
|
+
to_yaml,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .version import __version__
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"__version__",
|
|
17
|
+
"build_diff_context",
|
|
18
|
+
"map_directory",
|
|
19
|
+
"run",
|
|
20
|
+
"to_json",
|
|
21
|
+
"to_markdown",
|
|
22
|
+
"to_text",
|
|
23
|
+
"to_yaml",
|
|
24
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.0"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import io
|
|
5
|
+
import subprocess
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from treemapper.cli import main
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class CliResult:
|
|
16
|
+
stdout: str
|
|
17
|
+
stderr: str
|
|
18
|
+
exit_code: int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run_cli(argv: list[str]) -> CliResult:
|
|
22
|
+
out, err = io.StringIO(), io.StringIO()
|
|
23
|
+
exit_code = 0
|
|
24
|
+
with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
|
|
25
|
+
try:
|
|
26
|
+
main(argv)
|
|
27
|
+
except SystemExit as exc:
|
|
28
|
+
code = exc.code
|
|
29
|
+
exit_code = code if isinstance(code, int) else (0 if code is None else 1)
|
|
30
|
+
return CliResult(stdout=out.getvalue(), stderr=err.getvalue(), exit_code=exit_code)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture
|
|
34
|
+
def sample_project(tmp_path: Path) -> Path:
|
|
35
|
+
(tmp_path / "alpha.py").write_text("def alpha():\n return 1\n", encoding="utf-8")
|
|
36
|
+
(tmp_path / "readme.txt").write_text("hello tree\n", encoding="utf-8")
|
|
37
|
+
nested = tmp_path / "pkg"
|
|
38
|
+
nested.mkdir()
|
|
39
|
+
(nested / "beta.py").write_text("def beta():\n return 2\n", encoding="utf-8")
|
|
40
|
+
return tmp_path
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _git(repo: Path, *args: str) -> None:
|
|
44
|
+
subprocess.run(["git", *args], cwd=repo, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.fixture
|
|
48
|
+
def git_repo(tmp_path: Path) -> Path:
|
|
49
|
+
_git(tmp_path, "init")
|
|
50
|
+
_git(tmp_path, "config", "user.email", "test@example.com")
|
|
51
|
+
_git(tmp_path, "config", "user.name", "Test")
|
|
52
|
+
target = tmp_path / "module.py"
|
|
53
|
+
target.write_text("def compute(x):\n return x + 1\n", encoding="utf-8")
|
|
54
|
+
_git(tmp_path, "add", "-A")
|
|
55
|
+
_git(tmp_path, "commit", "-m", "initial")
|
|
56
|
+
target.write_text("def compute(x):\n return x + 2\n\n\ndef extra(y):\n return y * 2\n", encoding="utf-8")
|
|
57
|
+
_git(tmp_path, "add", "-A")
|
|
58
|
+
_git(tmp_path, "commit", "-m", "change compute and add extra")
|
|
59
|
+
return tmp_path
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import diffctx
|
|
6
|
+
|
|
7
|
+
import treemapper
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_public_api_is_exported() -> None:
|
|
11
|
+
for name in ("map_directory", "build_diff_context", "run", "to_yaml", "to_json", "to_text", "to_markdown"):
|
|
12
|
+
assert hasattr(treemapper, name), name
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_run_is_the_engine_entry() -> None:
|
|
16
|
+
assert treemapper.run is diffctx.run
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_map_directory_round_trips_to_yaml(sample_project: Path) -> None:
|
|
20
|
+
tree = treemapper.map_directory(str(sample_project))
|
|
21
|
+
assert tree["type"] == "directory"
|
|
22
|
+
rendered = treemapper.to_yaml(tree)
|
|
23
|
+
assert "alpha.py" in rendered
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_build_diff_context_on_real_repo(git_repo: Path) -> None:
|
|
27
|
+
context = treemapper.build_diff_context(root_dir=git_repo, diff_range="HEAD~1")
|
|
28
|
+
assert isinstance(context, dict)
|
|
29
|
+
rendered = treemapper.to_yaml(context)
|
|
30
|
+
assert "module.py" in rendered
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from treemapper.version import __version__
|
|
11
|
+
|
|
12
|
+
from .conftest import CliResult, run_cli
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_version_is_treemapper_branded() -> None:
|
|
16
|
+
result = run_cli(["--version"])
|
|
17
|
+
assert result.exit_code == 0
|
|
18
|
+
assert result.stdout.strip() == f"treemapper {__version__}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_help_renders_and_is_treemapper_branded() -> None:
|
|
22
|
+
result = run_cli(["--help"])
|
|
23
|
+
assert result.exit_code == 0
|
|
24
|
+
assert "--diff" in result.stdout
|
|
25
|
+
assert "usage: treemapper" in result.stdout.lower()
|
|
26
|
+
assert "usage: diffctx" not in result.stdout.lower()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_yaml_tree_includes_files_and_content(sample_project: Path) -> None:
|
|
30
|
+
result = run_cli([str(sample_project)])
|
|
31
|
+
assert result.exit_code == 0
|
|
32
|
+
tree = yaml.safe_load(result.stdout)
|
|
33
|
+
assert tree["type"] == "directory"
|
|
34
|
+
names = {child["name"] for child in tree["children"]}
|
|
35
|
+
assert "alpha.py" in names
|
|
36
|
+
assert "pkg" in names
|
|
37
|
+
assert "def alpha()" in result.stdout
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_json_format(sample_project: Path) -> None:
|
|
41
|
+
result = run_cli([str(sample_project), "-f", "json"])
|
|
42
|
+
assert result.exit_code == 0
|
|
43
|
+
tree = json.loads(result.stdout)
|
|
44
|
+
assert tree["type"] == "directory"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_no_content_omits_file_bodies(sample_project: Path) -> None:
|
|
48
|
+
result = run_cli([str(sample_project), "--no-content"])
|
|
49
|
+
assert result.exit_code == 0
|
|
50
|
+
assert "def alpha()" not in result.stdout
|
|
51
|
+
assert "alpha.py" in result.stdout
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_save_writes_file(sample_project: Path, monkeypatch) -> None:
|
|
55
|
+
monkeypatch.chdir(sample_project)
|
|
56
|
+
result = run_cli([str(sample_project), "-f", "md", "--save"])
|
|
57
|
+
assert result.exit_code == 0
|
|
58
|
+
assert (sample_project / "tree.md").is_file()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_diff_mode_selects_changed_context(git_repo: Path, monkeypatch) -> None:
|
|
62
|
+
monkeypatch.chdir(git_repo)
|
|
63
|
+
result = run_cli([str(git_repo), "--diff", "HEAD~1"])
|
|
64
|
+
assert result.exit_code == 0
|
|
65
|
+
assert "module.py" in result.stdout
|
|
66
|
+
assert "extra" in result.stdout
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_console_script_entry_point(sample_project: Path) -> None:
|
|
70
|
+
proc = subprocess.run(
|
|
71
|
+
[sys.executable, "-m", "treemapper", str(sample_project), "--no-content"],
|
|
72
|
+
capture_output=True,
|
|
73
|
+
text=True,
|
|
74
|
+
check=False,
|
|
75
|
+
)
|
|
76
|
+
assert proc.returncode == 0
|
|
77
|
+
assert "alpha.py" in proc.stdout
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_result_dataclass_shape() -> None:
|
|
81
|
+
result = run_cli(["--version"])
|
|
82
|
+
assert isinstance(result, CliResult)
|