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.
- {trls_cli-0.3.0 → trls_cli-0.4.0}/.gitignore +1 -0
- {trls_cli-0.3.0 → trls_cli-0.4.0}/PKG-INFO +14 -9
- {trls_cli-0.3.0 → trls_cli-0.4.0}/README.md +13 -8
- {trls_cli-0.3.0 → trls_cli-0.4.0}/RELEASING.md +4 -4
- {trls_cli-0.3.0 → trls_cli-0.4.0}/pyproject.toml +1 -1
- {trls_cli-0.3.0 → trls_cli-0.4.0}/src/trls/__init__.py +1 -1
- {trls_cli-0.3.0 → trls_cli-0.4.0}/src/trls/renderers.py +25 -48
- {trls_cli-0.3.0 → trls_cli-0.4.0}/tests/test_cli.py +4 -5
- {trls_cli-0.3.0 → trls_cli-0.4.0}/tests/test_tree.py +6 -9
- {trls_cli-0.3.0 → trls_cli-0.4.0}/LICENSE +0 -0
- {trls_cli-0.3.0 → trls_cli-0.4.0}/src/trls/__main__.py +0 -0
- {trls_cli-0.3.0 → trls_cli-0.4.0}/src/trls/cli.py +0 -0
- {trls_cli-0.3.0 → trls_cli-0.4.0}/src/trls/clipboard.py +0 -0
- {trls_cli-0.3.0 → trls_cli-0.4.0}/src/trls/snapshot.py +0 -0
- {trls_cli-0.3.0 → trls_cli-0.4.0}/src/trls/tree.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trls-cli
|
|
3
|
-
Version: 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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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.
|
|
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
|
|
227
|
-
-
|
|
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.
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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.
|
|
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
|
|
201
|
-
-
|
|
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.
|
|
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.
|
|
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.
|
|
83
|
-
git push origin v0.
|
|
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.
|
|
100
|
+
6. Push the `v0.4.0` tag to trigger the production release.
|
|
@@ -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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
168
|
+
def _collect_grouped_copy_entries(
|
|
184
169
|
node: TreeNode,
|
|
185
170
|
*,
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
|
117
|
-
assert "
|
|
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 "
|
|
217
|
-
assert "
|
|
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 "
|
|
73
|
-
assert "
|
|
74
|
-
assert "
|
|
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 "
|
|
146
|
-
assert "
|
|
147
|
-
assert "
|
|
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
|