treex-cli 0.1.11__tar.gz → 0.1.12__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: treex-cli
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: List directory contents as a tree with file and directory metadata
5
5
  Author: Corey Goldberg
6
6
  Maintainer: Corey Goldberg
@@ -37,15 +37,19 @@ Dynamic: license-file
37
37
 
38
38
  ## About
39
39
 
40
- `treex` is a command-line utility that recursively scans a directory and
41
- lists its contents as a tree along with file and directory metadata.
40
+ `treex` is a command-line utility that recursively scans a directory and lists
41
+ its contents as a tree along with file and directory metadata.
42
42
 
43
+ ### Features:
44
+
45
+ - Respects `.gitignore` rules when Git is installed
43
46
  - Shows human-readable file sizes
44
47
  - Shows recursive directory sizes
45
48
  - Shows line counts for text files
46
49
  - Identifies binary and unreadable files by type
47
50
  - Optionally shows modification timestamps
48
- - Respects `.gitignore` rules when Git is installed
51
+ - Optionally includes or excludes files using glob patterns
52
+ - Optionally limits the depth of the displayed tree
49
53
 
50
54
  ----
51
55
 
@@ -90,32 +94,80 @@ lists its contents as a tree along with file and directory metadata.
90
94
 
91
95
  ## Installation
92
96
 
93
- Install `treex` from [PyPI][pypi-home] using either `pip` or `pipx`:
97
+ Install `treex` from [PyPI][pypi-home] using your preferred Python package manager:
94
98
 
95
- - `pip install treex-cli`
96
- - `pipx install treex-cli`
99
+ - **pip:** `pip install treex-cli`
100
+ - **pipx:** `pipx install treex-cli`
101
+ - **uv:** `uv tool install treex-cli`
97
102
 
98
103
  ----
99
104
 
100
105
  ## Usage
101
106
 
102
107
  ```
103
- $ treex --help
104
- usage: treex [-h] [-a] [-m] [-q] [-d] [-s] [directory]
108
+ usage: treex [-h] [--all] [--modified] [--quiet] [--dirs] [--summary]
109
+ [--include PATTERN] [--exclude PATTERN] [--depth DEPTH]
110
+ [directory]
105
111
 
106
112
  List directory contents as a tree with file and directory metadata.
107
113
 
108
114
  positional arguments:
109
- directory directory to scan (default: current directory)
115
+ directory directory to scan (default: current directory)
110
116
 
111
117
  options:
112
- -h, --help show this help message and exit
113
- -a, --all show all files, including those ignored by Git
114
- -m, --modified show file modification times
115
- -q, --quiet don't show file metadata
116
- -d, --dirs show directories only
117
- -s, --summary show summary only
118
+ -h, --help show this help message and exit
119
+ --all, -a show all files, including those ignored by Git
120
+ --modified, -m show file modification times
121
+ --quiet, -q don't show file metadata
122
+ --dirs, -d show directories only
123
+ --summary, -s show summary only
124
+ --include, -i PATTERN include files matching a glob pattern
125
+ --exclude, -e PATTERN exclude files matching a glob pattern
126
+ --depth, -D DEPTH limit levels displayed below the starting directory
127
+ ```
128
+
129
+ ### Filtering files
130
+
131
+ Use `--include` to display only files that match a glob pattern:
132
+
118
133
  ```
134
+ treex --include '*.py'
135
+ ```
136
+
137
+ Use `--exclude` to hide files that match a glob pattern:
138
+
139
+ ```
140
+ treex --exclude '*.pyc'
141
+ ```
142
+
143
+ Patterns match filenames only, regardless of their location in the directory
144
+ tree. Matching is case-sensitive. When both options are provided, exclusions
145
+ take precedence over inclusions.
146
+
147
+ For example:
148
+
149
+ ```
150
+ treex --include '.py' --exclude 'test_.py'
151
+ ```
152
+
153
+ This displays Python files except those whose filenames begin with `test_`.
154
+
155
+ Note: quote glob patterns to prevent the shell from expanding them before
156
+ `treex` receives them.
157
+
158
+ ### Limiting tree depth
159
+
160
+ Use `--depth` (or `-D`) to limit how many levels below the starting directory
161
+ are displayed:
162
+
163
+ ```
164
+ treex --depth 3
165
+ ```
166
+
167
+ The depth must be at least 1.
168
+
169
+ Directory sizes and summary totals reflect only files within the limit that pass
170
+ the include/exclude filters.
119
171
 
120
172
  ----
121
173
 
@@ -123,6 +175,7 @@ options:
123
175
 
124
176
  ![Screenshot](https://raw.githubusercontent.com/cgoldberg/treex/refs/heads/main/screenshot.png)
125
177
 
178
+
126
179
  [github-profile]: https://github.com/cgoldberg
127
180
  [github-repo]: https://github.com/cgoldberg/treex
128
181
  [pypi-home]: https://pypi.org/project/treex-cli
@@ -0,0 +1,156 @@
1
+ # treex
2
+
3
+ ## List directory contents as a tree with file and directory metadata
4
+
5
+ - Copyright (c) 2026 [Corey Goldberg][github-profile]
6
+ - Development: [GitHub][github-repo]
7
+ - Releases: [PyPI][pypi-home]
8
+ - License: [MIT][mit-license]
9
+
10
+ ----
11
+
12
+ ## About
13
+
14
+ `treex` is a command-line utility that recursively scans a directory and lists
15
+ its contents as a tree along with file and directory metadata.
16
+
17
+ ### Features:
18
+
19
+ - Respects `.gitignore` rules when Git is installed
20
+ - Shows human-readable file sizes
21
+ - Shows recursive directory sizes
22
+ - Shows line counts for text files
23
+ - Identifies binary and unreadable files by type
24
+ - Optionally shows modification timestamps
25
+ - Optionally includes or excludes files using glob patterns
26
+ - Optionally limits the depth of the displayed tree
27
+
28
+ ----
29
+
30
+ ## Status
31
+
32
+ <table>
33
+ <tr>
34
+ <td>Latest Version</td>
35
+ <td>
36
+ <a href="https://pypi.org/project/treex-cli">
37
+ <img src="https://img.shields.io/pypi/v/treex-cli.svg">
38
+ </a>
39
+ </td>
40
+ </tr>
41
+ <tr>
42
+ <td>Tests (CI)</td>
43
+ <td>
44
+ <a href="https://github.com/cgoldberg/treex/actions/workflows/test.yml">
45
+ <img src="https://github.com/cgoldberg/treex/actions/workflows/test.yml/badge.svg">
46
+ </a>
47
+ </td>
48
+ </tr>
49
+ <tr>
50
+ <td>Supported Python Versions</td>
51
+ <td>
52
+ <a href="https://pypi.org/project/treex-cli">
53
+ <img src="https://img.shields.io/pypi/pyversions/treex-cli">
54
+ </a>
55
+ </td>
56
+ </tr>
57
+ </table>
58
+
59
+ ----
60
+
61
+ ## Requirements
62
+
63
+ - Python 3.10+
64
+ - Git 2.0+ (optional)
65
+ - UTF-8-compatible terminal
66
+
67
+ ----
68
+
69
+ ## Installation
70
+
71
+ Install `treex` from [PyPI][pypi-home] using your preferred Python package manager:
72
+
73
+ - **pip:** `pip install treex-cli`
74
+ - **pipx:** `pipx install treex-cli`
75
+ - **uv:** `uv tool install treex-cli`
76
+
77
+ ----
78
+
79
+ ## Usage
80
+
81
+ ```
82
+ usage: treex [-h] [--all] [--modified] [--quiet] [--dirs] [--summary]
83
+ [--include PATTERN] [--exclude PATTERN] [--depth DEPTH]
84
+ [directory]
85
+
86
+ List directory contents as a tree with file and directory metadata.
87
+
88
+ positional arguments:
89
+ directory directory to scan (default: current directory)
90
+
91
+ options:
92
+ -h, --help show this help message and exit
93
+ --all, -a show all files, including those ignored by Git
94
+ --modified, -m show file modification times
95
+ --quiet, -q don't show file metadata
96
+ --dirs, -d show directories only
97
+ --summary, -s show summary only
98
+ --include, -i PATTERN include files matching a glob pattern
99
+ --exclude, -e PATTERN exclude files matching a glob pattern
100
+ --depth, -D DEPTH limit levels displayed below the starting directory
101
+ ```
102
+
103
+ ### Filtering files
104
+
105
+ Use `--include` to display only files that match a glob pattern:
106
+
107
+ ```
108
+ treex --include '*.py'
109
+ ```
110
+
111
+ Use `--exclude` to hide files that match a glob pattern:
112
+
113
+ ```
114
+ treex --exclude '*.pyc'
115
+ ```
116
+
117
+ Patterns match filenames only, regardless of their location in the directory
118
+ tree. Matching is case-sensitive. When both options are provided, exclusions
119
+ take precedence over inclusions.
120
+
121
+ For example:
122
+
123
+ ```
124
+ treex --include '.py' --exclude 'test_.py'
125
+ ```
126
+
127
+ This displays Python files except those whose filenames begin with `test_`.
128
+
129
+ Note: quote glob patterns to prevent the shell from expanding them before
130
+ `treex` receives them.
131
+
132
+ ### Limiting tree depth
133
+
134
+ Use `--depth` (or `-D`) to limit how many levels below the starting directory
135
+ are displayed:
136
+
137
+ ```
138
+ treex --depth 3
139
+ ```
140
+
141
+ The depth must be at least 1.
142
+
143
+ Directory sizes and summary totals reflect only files within the limit that pass
144
+ the include/exclude filters.
145
+
146
+ ----
147
+
148
+ ## Screenshot
149
+
150
+ ![Screenshot](https://raw.githubusercontent.com/cgoldberg/treex/refs/heads/main/screenshot.png)
151
+
152
+
153
+ [github-profile]: https://github.com/cgoldberg
154
+ [github-repo]: https://github.com/cgoldberg/treex
155
+ [pypi-home]: https://pypi.org/project/treex-cli
156
+ [mit-license]: https://raw.githubusercontent.com/cgoldberg/treex/refs/heads/main/LICENSE
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "treex-cli"
7
- version = "0.1.11"
7
+ version = "0.1.12"
8
8
  description="List directory contents as a tree with file and directory metadata"
9
9
  license = "MIT"
10
10
  license-files = ["LICENSE"]
@@ -65,7 +65,7 @@ target-version = "py310"
65
65
  select = ["ALL"]
66
66
  ignore = [
67
67
  "ANN", "C901", "COM812", "D", "DTZ001", "DTZ006",
68
- "FBT", "PLR", "S603", "S607", "T201"
68
+ "FBT", "PLR", "S603", "S607", "T201",
69
69
  ]
70
70
  fixable = ["ALL"]
71
71
 
@@ -13,6 +13,7 @@ filesystem is scanned normally without Git filtering.
13
13
  """
14
14
 
15
15
  import argparse
16
+ import fnmatch
16
17
  import subprocess
17
18
  import sys
18
19
  from contextlib import suppress
@@ -104,10 +105,10 @@ def _is_binary_file(path, chunk_size=8192):
104
105
  chunk = f.read(chunk_size)
105
106
  if not chunk:
106
107
  return False
107
- # Check for NUL bytes.
108
+ # Check for NUL bytes
108
109
  if b"\x00" in chunk:
109
110
  return True
110
- # Treat files that aren't valid UTF-8 as binary.
111
+ # Treat files that aren't valid UTF-8 as binary
111
112
  try:
112
113
  chunk.decode("utf-8")
113
114
  except UnicodeDecodeError:
@@ -169,71 +170,181 @@ def _get_tree_entries(path, gitignore):
169
170
  return entries
170
171
 
171
172
 
172
- def _collect_tree(
173
+ def _matches_pattern(name, pattern):
174
+ """Match a filename using a case-sensitive glob."""
175
+ return pattern is not None and fnmatch.fnmatchcase(name, pattern)
176
+
177
+
178
+ def _scan_tree(
173
179
  path,
174
- prefix,
175
- stats,
176
180
  gitignore,
177
- rows,
178
181
  show_metadata,
179
182
  show_modified,
180
- show_files,
183
+ include_pattern=None,
184
+ exclude_pattern=None,
185
+ max_depth=None,
186
+ depth=0,
181
187
  ):
182
- """Walk the tree once and collect the output rows."""
188
+ """Scan and filter the tree, returning nodes for rendering.
189
+
190
+ The starting directory has depth 0. Its immediate entries have
191
+ depth 1. Directories at the depth limit are shown without
192
+ descending into them. Directory sizes include only files within
193
+ the permitted depth that pass the include/exclude filters.
194
+ """
195
+ if max_depth is not None and depth >= max_depth:
196
+ return []
183
197
  entries = _get_tree_entries(path, gitignore)
184
198
  if entries is None:
185
- rows.append((prefix + "└── [permission denied]", None))
186
- return 0
187
- total_size = 0
188
- # When files are hidden, only directories determine which visible
189
- # entry is last. Files are still processed for stats and sizes.
190
- visible_entries = entries
191
- if not show_files:
192
- visible_entries = [entry for entry in entries if entry.is_dir()]
193
- visible_index = 0
199
+ return [
200
+ {
201
+ "name": "[permission denied]",
202
+ "kind": "error",
203
+ "children": [],
204
+ "size": 0,
205
+ "info": None,
206
+ "modified": None,
207
+ "contains_match": False,
208
+ }
209
+ ]
210
+ nodes = []
194
211
  for entry in entries:
195
- is_visible = show_files or entry.is_dir()
196
- if is_visible:
197
- is_last = visible_index == len(visible_entries) - 1
198
- visible_index += 1
199
- connector = "└── " if is_last else "├── "
200
- tree_name = prefix + connector + entry.name
201
- else:
202
- tree_name = None
212
+ entry_depth = depth + 1
203
213
  if entry.is_dir():
214
+ if max_depth is not None and entry_depth >= max_depth:
215
+ children = []
216
+ else:
217
+ children = _scan_tree(
218
+ entry,
219
+ gitignore,
220
+ show_metadata,
221
+ show_modified,
222
+ include_pattern,
223
+ exclude_pattern,
224
+ max_depth,
225
+ entry_depth,
226
+ )
227
+ contains_match = any(child["contains_match"] for child in children)
228
+ if include_pattern is not None and not contains_match:
229
+ continue
230
+ size = (
231
+ sum(child["size"] or 0 for child in children) if show_metadata else None
232
+ )
233
+ nodes.append(
234
+ {
235
+ "name": entry.name,
236
+ "kind": "directory",
237
+ "children": children,
238
+ "size": size,
239
+ "info": None,
240
+ "modified": None,
241
+ "contains_match": contains_match,
242
+ }
243
+ )
244
+ continue
245
+ if not entry.is_file():
246
+ continue
247
+ # Exclusions take precedence over inclusions
248
+ if _matches_pattern(entry.name, exclude_pattern):
249
+ continue
250
+ if include_pattern is not None and not _matches_pattern(
251
+ entry.name,
252
+ include_pattern,
253
+ ):
254
+ continue
255
+ if show_metadata:
256
+ size_text, info, size, modified = _file_metadata(entry)
257
+ else:
258
+ size_text, info, size, modified = None, None, None, None
259
+ nodes.append(
260
+ {
261
+ "name": entry.name,
262
+ "kind": "file",
263
+ "children": [],
264
+ "size": size,
265
+ "size_text": size_text,
266
+ "info": info,
267
+ "modified": modified if show_modified else None,
268
+ "contains_match": True,
269
+ }
270
+ )
271
+ return nodes
272
+
273
+
274
+ def _collect_stats(nodes, stats):
275
+ """Add node counts and sizes to the summary statistics."""
276
+ for node in nodes:
277
+ if node["kind"] == "directory":
204
278
  stats["directories"] += 1
205
- row_index = len(rows)
279
+ _collect_stats(node["children"], stats)
280
+ elif node["kind"] == "file":
281
+ stats["files"] += 1
282
+ if stats["total_size"] is not None:
283
+ stats["total_size"] += node["size"]
284
+
285
+
286
+ def _render_tree(nodes, prefix, rows, show_metadata, show_files):
287
+ """Render nodes as tree rows."""
288
+ visible = [node for node in nodes if show_files or node["kind"] != "file"]
289
+ for index, node in enumerate(visible):
290
+ is_last = index == len(visible) - 1
291
+ connector = "└── " if is_last else "├── "
292
+ tree_name = prefix + connector + node["name"]
293
+ if node["kind"] == "error":
206
294
  rows.append((tree_name, None))
207
- # Indent nested entries, preserving the tree's vertical branch.
295
+ continue
296
+ if node["kind"] == "directory":
297
+ metadata = (
298
+ (_format_size(node["size"]), None, None) if show_metadata else None
299
+ )
300
+ rows.append((tree_name, metadata))
208
301
  extension = " " if is_last else "│ "
209
- dir_size = _collect_tree(
210
- entry,
302
+ _render_tree(
303
+ node["children"],
211
304
  prefix + extension,
212
- stats,
213
- gitignore,
214
305
  rows,
215
306
  show_metadata,
216
- show_modified,
217
307
  show_files,
218
308
  )
219
- total_size += dir_size
220
- if show_metadata:
221
- rows[row_index] = (tree_name, (_format_size(dir_size), None, None))
222
- elif entry.is_file():
309
+ elif show_files:
310
+ metadata = None
223
311
  if show_metadata:
224
- size_text, info, size, modified = _file_metadata(entry)
225
- stats["files"] += 1
226
- stats["total_size"] += size
227
- total_size += size
228
- if show_files:
229
- timestamp = modified if show_modified else None
230
- rows.append((tree_name, (size_text, info, timestamp)))
231
- else:
232
- stats["files"] += 1
233
- stats["total_size"] = None
234
- if show_files:
235
- rows.append((tree_name, None))
236
- return total_size
312
+ metadata = (
313
+ node["size_text"],
314
+ node["info"],
315
+ node["modified"],
316
+ )
317
+ rows.append((tree_name, metadata))
318
+
319
+
320
+ def _collect_tree(
321
+ path,
322
+ prefix,
323
+ stats,
324
+ gitignore,
325
+ rows,
326
+ show_metadata,
327
+ show_modified,
328
+ show_files,
329
+ include_pattern=None,
330
+ exclude_pattern=None,
331
+ max_depth=None,
332
+ ):
333
+ """Collect filtered tree rows and update summary statistics."""
334
+ if not show_metadata:
335
+ stats["total_size"] = None
336
+ nodes = _scan_tree(
337
+ Path(path),
338
+ gitignore,
339
+ show_metadata,
340
+ show_modified,
341
+ include_pattern,
342
+ exclude_pattern,
343
+ max_depth,
344
+ )
345
+ _collect_stats(nodes, stats)
346
+ _render_tree(nodes, prefix, rows, show_metadata, show_files)
347
+ return sum(node["size"] or 0 for node in nodes)
237
348
 
238
349
 
239
350
  def print_tree(
@@ -245,12 +356,14 @@ def print_tree(
245
356
  show_modified=False,
246
357
  show_tree=True,
247
358
  show_files=True,
359
+ include_pattern=None,
360
+ exclude_pattern=None,
361
+ max_depth=None,
248
362
  ):
249
363
  """Print the directory tree with aligned metadata."""
250
364
  if stats is None:
251
365
  stats = {"directories": 0, "files": 0, "total_size": 0}
252
366
  rows = []
253
- # Walk the filesystem once and collect the output
254
367
  _collect_tree(
255
368
  Path(directory),
256
369
  prefix,
@@ -260,21 +373,25 @@ def print_tree(
260
373
  show_metadata,
261
374
  show_modified,
262
375
  show_files,
376
+ include_pattern,
377
+ exclude_pattern,
378
+ max_depth,
263
379
  )
264
380
  if show_tree:
265
381
  # Align metadata with the longest tree/name
266
- max_tree_name_width = max((len(tree_name) for tree_name, _ in rows), default=0)
382
+ max_width = max((len(tree_name) for tree_name, _ in rows), default=0)
267
383
  for tree_name, metadata in rows:
268
384
  if metadata is None:
269
385
  print(tree_name)
270
386
  continue
271
387
  size_text, info, modified = metadata
272
- output = f"{tree_name:<{max_tree_name_width}}{size_text:>10}"
388
+ output = f"{tree_name:<{max_width}}{size_text:>10}"
273
389
  if info is not None:
274
390
  output += f" {info:<12}"
275
391
  if modified is not None:
276
392
  output += f" {modified}"
277
393
  print(output)
394
+
278
395
  return stats
279
396
 
280
397
 
@@ -291,11 +408,33 @@ def print_summary(stats):
291
408
  print(summary)
292
409
 
293
410
 
411
+ def _positive_int(value):
412
+ """Parse a positive integer."""
413
+ try:
414
+ number = int(value)
415
+ except ValueError:
416
+ msg = "must be a positive integer"
417
+ raise argparse.ArgumentTypeError(msg) from None
418
+ if number < 1:
419
+ msg = "must be at least 1"
420
+ raise argparse.ArgumentTypeError(msg)
421
+ return number
422
+
423
+
294
424
  def parse_args(argv=None):
295
425
  """Parse command-line arguments."""
426
+
427
+ def formatter(prog):
428
+ return argparse.HelpFormatter(
429
+ prog,
430
+ width=80,
431
+ max_help_position=30,
432
+ )
433
+
296
434
  parser = argparse.ArgumentParser(
297
435
  description="List directory contents as a tree "
298
436
  "with file and directory metadata.",
437
+ formatter_class=formatter,
299
438
  )
300
439
  parser.add_argument(
301
440
  "directory",
@@ -305,35 +444,54 @@ def parse_args(argv=None):
305
444
  help="directory to scan (default: current directory)",
306
445
  )
307
446
  parser.add_argument(
308
- "-a",
309
447
  "--all",
448
+ "-a",
310
449
  action="store_true",
311
450
  help="show all files, including those ignored by Git",
312
451
  )
313
452
  parser.add_argument(
314
- "-m",
315
453
  "--modified",
454
+ "-m",
316
455
  action="store_true",
317
456
  help="show file modification times",
318
457
  )
319
458
  parser.add_argument(
320
- "-q",
321
459
  "--quiet",
460
+ "-q",
322
461
  action="store_true",
323
462
  help="don't show file metadata",
324
463
  )
325
464
  parser.add_argument(
326
- "-d",
327
465
  "--dirs",
466
+ "-d",
328
467
  action="store_true",
329
468
  help="show directories only",
330
469
  )
331
470
  parser.add_argument(
332
- "-s",
333
471
  "--summary",
472
+ "-s",
334
473
  action="store_true",
335
474
  help="show summary only",
336
475
  )
476
+ parser.add_argument(
477
+ "--include",
478
+ "-i",
479
+ metavar="PATTERN",
480
+ help="include files matching a glob pattern",
481
+ )
482
+ parser.add_argument(
483
+ "--exclude",
484
+ "-e",
485
+ metavar="PATTERN",
486
+ help="exclude files matching a glob pattern",
487
+ )
488
+ parser.add_argument(
489
+ "--depth",
490
+ "-D",
491
+ type=_positive_int,
492
+ metavar="DEPTH",
493
+ help="limit levels displayed below the starting directory",
494
+ )
337
495
  return parser.parse_args(argv)
338
496
 
339
497
 
@@ -358,6 +516,9 @@ def main():
358
516
  show_modified=args.modified,
359
517
  show_tree=show_tree,
360
518
  show_files=show_files,
519
+ include_pattern=args.include,
520
+ exclude_pattern=args.exclude,
521
+ max_depth=args.depth,
361
522
  )
362
523
  if show_tree:
363
524
  print()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: treex-cli
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: List directory contents as a tree with file and directory metadata
5
5
  Author: Corey Goldberg
6
6
  Maintainer: Corey Goldberg
@@ -37,15 +37,19 @@ Dynamic: license-file
37
37
 
38
38
  ## About
39
39
 
40
- `treex` is a command-line utility that recursively scans a directory and
41
- lists its contents as a tree along with file and directory metadata.
40
+ `treex` is a command-line utility that recursively scans a directory and lists
41
+ its contents as a tree along with file and directory metadata.
42
42
 
43
+ ### Features:
44
+
45
+ - Respects `.gitignore` rules when Git is installed
43
46
  - Shows human-readable file sizes
44
47
  - Shows recursive directory sizes
45
48
  - Shows line counts for text files
46
49
  - Identifies binary and unreadable files by type
47
50
  - Optionally shows modification timestamps
48
- - Respects `.gitignore` rules when Git is installed
51
+ - Optionally includes or excludes files using glob patterns
52
+ - Optionally limits the depth of the displayed tree
49
53
 
50
54
  ----
51
55
 
@@ -90,32 +94,80 @@ lists its contents as a tree along with file and directory metadata.
90
94
 
91
95
  ## Installation
92
96
 
93
- Install `treex` from [PyPI][pypi-home] using either `pip` or `pipx`:
97
+ Install `treex` from [PyPI][pypi-home] using your preferred Python package manager:
94
98
 
95
- - `pip install treex-cli`
96
- - `pipx install treex-cli`
99
+ - **pip:** `pip install treex-cli`
100
+ - **pipx:** `pipx install treex-cli`
101
+ - **uv:** `uv tool install treex-cli`
97
102
 
98
103
  ----
99
104
 
100
105
  ## Usage
101
106
 
102
107
  ```
103
- $ treex --help
104
- usage: treex [-h] [-a] [-m] [-q] [-d] [-s] [directory]
108
+ usage: treex [-h] [--all] [--modified] [--quiet] [--dirs] [--summary]
109
+ [--include PATTERN] [--exclude PATTERN] [--depth DEPTH]
110
+ [directory]
105
111
 
106
112
  List directory contents as a tree with file and directory metadata.
107
113
 
108
114
  positional arguments:
109
- directory directory to scan (default: current directory)
115
+ directory directory to scan (default: current directory)
110
116
 
111
117
  options:
112
- -h, --help show this help message and exit
113
- -a, --all show all files, including those ignored by Git
114
- -m, --modified show file modification times
115
- -q, --quiet don't show file metadata
116
- -d, --dirs show directories only
117
- -s, --summary show summary only
118
+ -h, --help show this help message and exit
119
+ --all, -a show all files, including those ignored by Git
120
+ --modified, -m show file modification times
121
+ --quiet, -q don't show file metadata
122
+ --dirs, -d show directories only
123
+ --summary, -s show summary only
124
+ --include, -i PATTERN include files matching a glob pattern
125
+ --exclude, -e PATTERN exclude files matching a glob pattern
126
+ --depth, -D DEPTH limit levels displayed below the starting directory
127
+ ```
128
+
129
+ ### Filtering files
130
+
131
+ Use `--include` to display only files that match a glob pattern:
132
+
118
133
  ```
134
+ treex --include '*.py'
135
+ ```
136
+
137
+ Use `--exclude` to hide files that match a glob pattern:
138
+
139
+ ```
140
+ treex --exclude '*.pyc'
141
+ ```
142
+
143
+ Patterns match filenames only, regardless of their location in the directory
144
+ tree. Matching is case-sensitive. When both options are provided, exclusions
145
+ take precedence over inclusions.
146
+
147
+ For example:
148
+
149
+ ```
150
+ treex --include '.py' --exclude 'test_.py'
151
+ ```
152
+
153
+ This displays Python files except those whose filenames begin with `test_`.
154
+
155
+ Note: quote glob patterns to prevent the shell from expanding them before
156
+ `treex` receives them.
157
+
158
+ ### Limiting tree depth
159
+
160
+ Use `--depth` (or `-D`) to limit how many levels below the starting directory
161
+ are displayed:
162
+
163
+ ```
164
+ treex --depth 3
165
+ ```
166
+
167
+ The depth must be at least 1.
168
+
169
+ Directory sizes and summary totals reflect only files within the limit that pass
170
+ the include/exclude filters.
119
171
 
120
172
  ----
121
173
 
@@ -123,6 +175,7 @@ options:
123
175
 
124
176
  ![Screenshot](https://raw.githubusercontent.com/cgoldberg/treex/refs/heads/main/screenshot.png)
125
177
 
178
+
126
179
  [github-profile]: https://github.com/cgoldberg
127
180
  [github-repo]: https://github.com/cgoldberg/treex
128
181
  [pypi-home]: https://pypi.org/project/treex-cli
@@ -1,103 +0,0 @@
1
- # treex
2
-
3
- ## List directory contents as a tree with file and directory metadata
4
-
5
- - Copyright (c) 2026 [Corey Goldberg][github-profile]
6
- - Development: [GitHub][github-repo]
7
- - Releases: [PyPI][pypi-home]
8
- - License: [MIT][mit-license]
9
-
10
- ----
11
-
12
- ## About
13
-
14
- `treex` is a command-line utility that recursively scans a directory and
15
- lists its contents as a tree along with file and directory metadata.
16
-
17
- - Shows human-readable file sizes
18
- - Shows recursive directory sizes
19
- - Shows line counts for text files
20
- - Identifies binary and unreadable files by type
21
- - Optionally shows modification timestamps
22
- - Respects `.gitignore` rules when Git is installed
23
-
24
- ----
25
-
26
- ## Status
27
-
28
- <table>
29
- <tr>
30
- <td>Latest Version</td>
31
- <td>
32
- <a href="https://pypi.org/project/treex-cli">
33
- <img src="https://img.shields.io/pypi/v/treex-cli.svg">
34
- </a>
35
- </td>
36
- </tr>
37
- <tr>
38
- <td>Tests (CI)</td>
39
- <td>
40
- <a href="https://github.com/cgoldberg/treex/actions/workflows/test.yml">
41
- <img src="https://github.com/cgoldberg/treex/actions/workflows/test.yml/badge.svg">
42
- </a>
43
- </td>
44
- </tr>
45
- <tr>
46
- <td>Supported Python Versions</td>
47
- <td>
48
- <a href="https://pypi.org/project/treex-cli">
49
- <img src="https://img.shields.io/pypi/pyversions/treex-cli">
50
- </a>
51
- </td>
52
- </tr>
53
- </table>
54
-
55
- ----
56
-
57
- ## Requirements
58
-
59
- - Python 3.10+
60
- - Git 2.0+ (optional)
61
- - UTF-8-compatible terminal
62
-
63
- ----
64
-
65
- ## Installation
66
-
67
- Install `treex` from [PyPI][pypi-home] using either `pip` or `pipx`:
68
-
69
- - `pip install treex-cli`
70
- - `pipx install treex-cli`
71
-
72
- ----
73
-
74
- ## Usage
75
-
76
- ```
77
- $ treex --help
78
- usage: treex [-h] [-a] [-m] [-q] [-d] [-s] [directory]
79
-
80
- List directory contents as a tree with file and directory metadata.
81
-
82
- positional arguments:
83
- directory directory to scan (default: current directory)
84
-
85
- options:
86
- -h, --help show this help message and exit
87
- -a, --all show all files, including those ignored by Git
88
- -m, --modified show file modification times
89
- -q, --quiet don't show file metadata
90
- -d, --dirs show directories only
91
- -s, --summary show summary only
92
- ```
93
-
94
- ----
95
-
96
- ## Screenshot
97
-
98
- ![Screenshot](https://raw.githubusercontent.com/cgoldberg/treex/refs/heads/main/screenshot.png)
99
-
100
- [github-profile]: https://github.com/cgoldberg
101
- [github-repo]: https://github.com/cgoldberg/treex
102
- [pypi-home]: https://pypi.org/project/treex-cli
103
- [mit-license]: https://raw.githubusercontent.com/cgoldberg/treex/refs/heads/main/LICENSE
File without changes
File without changes