trls-cli 0.3.0__tar.gz → 0.4.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.
@@ -1,4 +1,5 @@
1
1
  .pytest_cache/
2
+ .env
2
3
  .venv/
3
4
  build/
4
5
  dist/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trls-cli
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: A modern tree-style CLI for humans and AI prompts.
5
5
  Author: Yonglin and Yuanben
6
6
  License: MIT
@@ -180,10 +180,9 @@ Clipboard copy output with `-c`:
180
180
 
181
181
  ```text
182
182
  project/
183
- paths: project/src/, project/src/trls/, project/src/trls/cli.py, project/README.md
184
- modified: project/README.md
185
- added: project/src/new_file.py
186
- removed: project/src/old_file.py
183
+ src/trls/: cli.py
184
+ root: ~README.md
185
+ src/: +new_file.py, -old_file.py
187
186
  ```
188
187
 
189
188
  ## Python API
@@ -207,7 +206,7 @@ Diff markers:
207
206
  - `-` removed file or directory
208
207
  - `~` modified file or directory
209
208
 
210
- Current behavior in `v0.3.0`:
209
+ Current behavior in `v0.4.0`:
211
210
 
212
211
  - every successful run updates the latest snapshot for that path
213
212
  - default `trls` output compares against the previous run for that path
@@ -223,8 +222,9 @@ Use `trls -c` to keep the normal terminal render while also copying a compact pr
223
222
  Clipboard behavior:
224
223
 
225
224
  - the first line keeps the root directory name
226
- - later lines compress paths into small labeled blocks such as `paths:`, `modified:`, `added:`, and `removed:`
227
- - full root-prefixed paths are preserved to keep context for LLMs
225
+ - later lines group entries by directory, such as `src/: cli.py, tree.py`
226
+ - top-level files are collected under `root:`
227
+ - `+`, `-`, and `~` stay inline with each item to preserve diff meaning
228
228
  - the clipboard payload is intentionally different from the terminal render to save tokens
229
229
  - WSL-aware clipboard fallbacks are attempted before reporting an error
230
230
 
@@ -236,7 +236,7 @@ Clipboard behavior:
236
236
  - spreadsheet and office files such as `.xlsx`, `.csv`, `.docx`, and `.pptx` have their own color family
237
237
  - data-oriented files such as `.parquet` and `.ipynb` are also colorized
238
238
 
239
- ## CLI contract for `v0.3.0`
239
+ ## CLI contract for `v0.4.0`
240
240
 
241
241
  The first public release guarantees:
242
242
 
@@ -290,4 +290,9 @@ Recommended flow:
290
290
  2. Verify `pip install`, `trls --version`, and one real CLI example.
291
291
  3. Publish the tagged release to PyPI with Trusted Publishing.
292
292
 
293
+ Remove-Item -Recurse -Force .\dist
294
+ python -m pytest
295
+ python -m build
296
+ python -m twine check dist/*
297
+ python -m twine upload --disable-progress-bar dist/*
293
298
  See `RELEASING.md` and `.github/workflows/publish.yml` for the release checklist.
@@ -154,10 +154,9 @@ Clipboard copy output with `-c`:
154
154
 
155
155
  ```text
156
156
  project/
157
- paths: project/src/, project/src/trls/, project/src/trls/cli.py, project/README.md
158
- modified: project/README.md
159
- added: project/src/new_file.py
160
- removed: project/src/old_file.py
157
+ src/trls/: cli.py
158
+ root: ~README.md
159
+ src/: +new_file.py, -old_file.py
161
160
  ```
162
161
 
163
162
  ## Python API
@@ -181,7 +180,7 @@ Diff markers:
181
180
  - `-` removed file or directory
182
181
  - `~` modified file or directory
183
182
 
184
- Current behavior in `v0.3.0`:
183
+ Current behavior in `v0.4.0`:
185
184
 
186
185
  - every successful run updates the latest snapshot for that path
187
186
  - default `trls` output compares against the previous run for that path
@@ -197,8 +196,9 @@ Use `trls -c` to keep the normal terminal render while also copying a compact pr
197
196
  Clipboard behavior:
198
197
 
199
198
  - the first line keeps the root directory name
200
- - later lines compress paths into small labeled blocks such as `paths:`, `modified:`, `added:`, and `removed:`
201
- - full root-prefixed paths are preserved to keep context for LLMs
199
+ - later lines group entries by directory, such as `src/: cli.py, tree.py`
200
+ - top-level files are collected under `root:`
201
+ - `+`, `-`, and `~` stay inline with each item to preserve diff meaning
202
202
  - the clipboard payload is intentionally different from the terminal render to save tokens
203
203
  - WSL-aware clipboard fallbacks are attempted before reporting an error
204
204
 
@@ -210,7 +210,7 @@ Clipboard behavior:
210
210
  - spreadsheet and office files such as `.xlsx`, `.csv`, `.docx`, and `.pptx` have their own color family
211
211
  - data-oriented files such as `.parquet` and `.ipynb` are also colorized
212
212
 
213
- ## CLI contract for `v0.3.0`
213
+ ## CLI contract for `v0.4.0`
214
214
 
215
215
  The first public release guarantees:
216
216
 
@@ -264,4 +264,9 @@ Recommended flow:
264
264
  2. Verify `pip install`, `trls --version`, and one real CLI example.
265
265
  3. Publish the tagged release to PyPI with Trusted Publishing.
266
266
 
267
+ Remove-Item -Recurse -Force .\dist
268
+ python -m pytest
269
+ python -m build
270
+ python -m twine check dist/*
271
+ python -m twine upload --disable-progress-bar dist/*
267
272
  See `RELEASING.md` and `.github/workflows/publish.yml` for the release checklist.
@@ -6,7 +6,7 @@ This repository is prepared for a CLI-first PyPI release.
6
6
 
7
7
  - distribution name: `trls-cli`
8
8
  - import package: `trls`
9
- - version: `0.3.0`
9
+ - version: `0.4.0`
10
10
  - release channel: TestPyPI first, then PyPI
11
11
 
12
12
  At the time of preparation, `https://pypi.org/project/trls-cli/` should be re-checked right before uploading.
@@ -79,8 +79,8 @@ The workflow already expects the `pypi` environment in GitHub Actions.
79
79
  After Trusted Publishing is configured:
80
80
 
81
81
  ```bash
82
- git tag v0.3.0
83
- git push origin v0.3.0
82
+ git tag v0.4.0
83
+ git push origin v0.4.0
84
84
  ```
85
85
 
86
86
  The GitHub Actions workflow will:
@@ -97,4 +97,4 @@ The GitHub Actions workflow will:
97
97
  3. Add the real repository URLs to `pyproject.toml`.
98
98
  4. Ensure README examples still match current CLI behavior.
99
99
  5. Upload to TestPyPI and validate installation.
100
- 6. Push the `v0.3.0` tag to trigger the production release.
100
+ 6. Push the `v0.4.0` tag to trigger the production release.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "trls-cli"
7
- version = "0.3.0"
7
+ version = "0.4.0"
8
8
  description = "A modern tree-style CLI for humans and AI prompts."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -8,7 +8,7 @@ from trls.renderers import (
8
8
  )
9
9
  from trls.tree import TreeNode, scan_tree
10
10
 
11
- __version__ = "0.3.0"
11
+ __version__ = "0.4.0"
12
12
 
13
13
  __all__ = [
14
14
  "TreeNode",
@@ -96,29 +96,14 @@ def render_prompt(node: TreeNode) -> str:
96
96
 
97
97
  def render_compact_copy(node: TreeNode) -> str:
98
98
  lines = [node.display_name]
99
- current_paths: list[str] = []
100
- added_paths: list[str] = []
101
- modified_paths: list[str] = []
102
- removed_paths: list[str] = []
99
+ grouped_entries: dict[str, list[str]] = {}
103
100
 
104
101
  for child in node.children:
105
- _collect_compact_copy_entries(
106
- child,
107
- prefix=node.display_name,
108
- current_paths=current_paths,
109
- added_paths=added_paths,
110
- modified_paths=modified_paths,
111
- removed_paths=removed_paths,
112
- )
113
-
114
- if current_paths:
115
- lines.append(f"paths: {', '.join(current_paths)}")
116
- if modified_paths:
117
- lines.append(f"modified: {', '.join(modified_paths)}")
118
- if added_paths:
119
- lines.append(f"added: {', '.join(added_paths)}")
120
- if removed_paths:
121
- lines.append(f"removed: {', '.join(removed_paths)}")
102
+ _collect_grouped_copy_entries(child, grouped_entries=grouped_entries, parent_dir="")
103
+
104
+ for group_name in sorted(grouped_entries, key=lambda key: (key == "", key.lower())):
105
+ label = "root" if group_name == "" else f"{group_name}/"
106
+ lines.append(f"{label}: {', '.join(grouped_entries[group_name])}")
122
107
 
123
108
  return "\n".join(lines)
124
109
 
@@ -180,36 +165,28 @@ def _append_prompt_lines(node: TreeNode, lines: list[str], depth: int) -> None:
180
165
  _append_prompt_lines(child, lines, depth + 1)
181
166
 
182
167
 
183
- def _collect_compact_copy_entries(
168
+ def _collect_grouped_copy_entries(
184
169
  node: TreeNode,
185
170
  *,
186
- prefix: str,
187
- current_paths: list[str],
188
- added_paths: list[str],
189
- modified_paths: list[str],
190
- removed_paths: list[str],
171
+ grouped_entries: dict[str, list[str]],
172
+ parent_dir: str,
191
173
  ) -> None:
192
- full_path = f"{prefix}{node.name}/" if node.is_dir else f"{prefix}{node.name}"
193
-
194
- if node.diff_status != "removed":
195
- current_paths.append(full_path)
196
- if node.diff_status == "added":
197
- added_paths.append(full_path)
198
- elif node.diff_status == "modified":
199
- modified_paths.append(full_path)
200
- elif node.diff_status == "removed":
201
- removed_paths.append(full_path)
202
-
203
- child_prefix = full_path if node.is_dir else prefix
204
- for child in node.children:
205
- _collect_compact_copy_entries(
206
- child,
207
- prefix=child_prefix,
208
- current_paths=current_paths,
209
- added_paths=added_paths,
210
- modified_paths=modified_paths,
211
- removed_paths=removed_paths,
212
- )
174
+ if node.is_dir and node.children:
175
+ next_parent_dir = node.name if not parent_dir else f"{parent_dir}/{node.name}"
176
+ for child in node.children:
177
+ _collect_grouped_copy_entries(
178
+ child,
179
+ grouped_entries=grouped_entries,
180
+ parent_dir=next_parent_dir,
181
+ )
182
+ return
183
+
184
+ group_name = parent_dir
185
+ entry_name = node.display_name if node.is_dir else node.name
186
+ marker = _diff_marker(node).strip()
187
+ if marker:
188
+ entry_name = f"{marker}{entry_name}"
189
+ grouped_entries.setdefault(group_name, []).append(entry_name)
213
190
 
214
191
 
215
192
  def _prompt_tag(node: TreeNode) -> str:
@@ -113,8 +113,8 @@ def test_cli_copy_writes_compact_clipboard_output(capsys, monkeypatch, tmp_path)
113
113
  assert "[dir] demo/" in captured.out
114
114
  clipboard_lines = copied["text"].splitlines()
115
115
  assert clipboard_lines[0] == "demo/"
116
- assert clipboard_lines[1].startswith("paths: ")
117
- assert "demo/src/main.py" in copied["text"]
116
+ assert "src/: main.py" in copied["text"]
117
+ assert "root: notes.md" in copied["text"]
118
118
  assert copied["text"] != captured.out.strip()
119
119
 
120
120
 
@@ -213,9 +213,8 @@ def test_cli_copy_preserves_diff_markers_in_clipboard(capsys, monkeypatch, tmp_p
213
213
 
214
214
  assert exit_code == 0
215
215
  assert "~ [doc] notes.md" in captured.out
216
- assert "modified: " in copied["text"]
217
- assert "demo/notes.md" in copied["text"]
218
- assert "added: demo/src/helper.py" in copied["text"]
216
+ assert "root: ~notes.md" in copied["text"]
217
+ assert "+helper.py" in copied["text"]
219
218
 
220
219
 
221
220
  def test_cli_copy_reports_clipboard_failure(capsys, monkeypatch, tmp_path):
@@ -69,9 +69,9 @@ def test_renderers_produce_stable_outputs(tmp_path):
69
69
  payload = json.loads(json_output)
70
70
 
71
71
  assert compact_output.splitlines()[0] == "project/"
72
- assert "paths: " in compact_output
73
- assert "project/src/app.py" in compact_output
74
- assert "project/photo.jpg" in compact_output
72
+ assert "src/: app.py" in compact_output
73
+ assert "root: " in compact_output
74
+ assert "photo.jpg" in compact_output
75
75
  assert "[dir] project/" in prompt_output
76
76
  assert "[doc] README.md" in prompt_output
77
77
  assert "README.md" in text_output
@@ -142,9 +142,6 @@ def test_snapshot_round_trip_and_diff_statuses(tmp_path):
142
142
 
143
143
  compact_output = render_compact_copy(diff_tree)
144
144
  assert compact_output.splitlines()[0] == "project/"
145
- assert "paths: " in compact_output
146
- assert "modified: " in compact_output
147
- assert "project/README.md" in compact_output
148
- assert "project/src/" in compact_output
149
- assert "added: project/src/new.py" in compact_output
150
- assert "removed: project/src/app.py" in compact_output
145
+ assert "src/: +new.py, -app.py" in compact_output or "src/: -app.py, +new.py" in compact_output
146
+ assert "root: " in compact_output
147
+ assert "~README.md" in compact_output
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes