exist-shell 0.1.0__tar.gz → 0.1.2__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.
Files changed (106) hide show
  1. {exist_shell-0.1.0 → exist_shell-0.1.2}/CHANGELOG.md +13 -0
  2. exist_shell-0.1.2/LICENSE +21 -0
  3. exist_shell-0.1.2/PKG-INFO +280 -0
  4. {exist_shell-0.1.0 → exist_shell-0.1.2}/README.md +23 -1
  5. {exist_shell-0.1.0 → exist_shell-0.1.2}/docs/api.md +8 -0
  6. {exist_shell-0.1.0 → exist_shell-0.1.2}/docs/index.md +1 -1
  7. {exist_shell-0.1.0 → exist_shell-0.1.2}/docs/installation.md +24 -2
  8. exist_shell-0.1.2/pyproject.toml +62 -0
  9. {exist_shell-0.1.0 → exist_shell-0.1.2}/uv.lock +1 -1
  10. exist_shell-0.1.0/PKG-INFO +0 -10
  11. exist_shell-0.1.0/pyproject.toml +0 -37
  12. {exist_shell-0.1.0 → exist_shell-0.1.2}/.gitguardian.yaml +0 -0
  13. {exist_shell-0.1.0 → exist_shell-0.1.2}/.github/dependabot.yml +0 -0
  14. {exist_shell-0.1.0 → exist_shell-0.1.2}/.github/workflows/docs.yml +0 -0
  15. {exist_shell-0.1.0 → exist_shell-0.1.2}/.github/workflows/e2e.yml +0 -0
  16. {exist_shell-0.1.0 → exist_shell-0.1.2}/.github/workflows/release.yml +0 -0
  17. {exist_shell-0.1.0 → exist_shell-0.1.2}/.github/workflows/ruff.yml +0 -0
  18. {exist_shell-0.1.0 → exist_shell-0.1.2}/.github/workflows/tests.yml +0 -0
  19. {exist_shell-0.1.0 → exist_shell-0.1.2}/.github/workflows/ty.yml +0 -0
  20. {exist_shell-0.1.0 → exist_shell-0.1.2}/.gitignore +0 -0
  21. {exist_shell-0.1.0 → exist_shell-0.1.2}/CLAUDE.md +0 -0
  22. {exist_shell-0.1.0 → exist_shell-0.1.2}/Makefile +0 -0
  23. {exist_shell-0.1.0 → exist_shell-0.1.2}/docs/commands.md +0 -0
  24. {exist_shell-0.1.0 → exist_shell-0.1.2}/docs/completion.md +0 -0
  25. {exist_shell-0.1.0 → exist_shell-0.1.2}/docs/configuration.md +0 -0
  26. {exist_shell-0.1.0 → exist_shell-0.1.2}/docs/development.md +0 -0
  27. {exist_shell-0.1.0 → exist_shell-0.1.2}/docs/sync.md +0 -0
  28. {exist_shell-0.1.0 → exist_shell-0.1.2}/mkdocs.yml +0 -0
  29. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/docker.sh +0 -0
  30. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/lib.sh +0 -0
  31. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T02_server.sh +0 -0
  32. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T03_collection.sh +0 -0
  33. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T04_ls.sh +0 -0
  34. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T05_put.sh +0 -0
  35. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T06_ls_after.sh +0 -0
  36. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T07_cat.sh +0 -0
  37. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T08_cp.sh +0 -0
  38. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T09_rm.sh +0 -0
  39. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T10_mkdir.sh +0 -0
  40. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T11_edit.sh +0 -0
  41. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T12_sync.sh +0 -0
  42. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T13_mv.sh +0 -0
  43. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T14_exec.sh +0 -0
  44. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T15_user.sh +0 -0
  45. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T16_group.sh +0 -0
  46. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T17_chown.sh +0 -0
  47. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e/sections/T18_chmod.sh +0 -0
  48. {exist_shell-0.1.0 → exist_shell-0.1.2}/scripts/e2e.sh +0 -0
  49. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/__init__.py +0 -0
  50. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/cache.py +0 -0
  51. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/client/__init__.py +0 -0
  52. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/client/_base.py +0 -0
  53. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/client/_collections.py +0 -0
  54. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/client/_documents.py +0 -0
  55. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/client/_groups.py +0 -0
  56. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/client/_permissions.py +0 -0
  57. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/client/_queries.py +0 -0
  58. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/client/_users.py +0 -0
  59. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/__init__.py +0 -0
  60. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/cat.py +0 -0
  61. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/chmod.py +0 -0
  62. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/chown.py +0 -0
  63. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/collection.py +0 -0
  64. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/cp.py +0 -0
  65. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/edit.py +0 -0
  66. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/exec.py +0 -0
  67. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/group.py +0 -0
  68. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/ls.py +0 -0
  69. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/mkdir.py +0 -0
  70. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/mv.py +0 -0
  71. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/put.py +0 -0
  72. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/rm.py +0 -0
  73. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/server.py +0 -0
  74. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/sync.py +0 -0
  75. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/commands/user.py +0 -0
  76. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/completions.py +0 -0
  77. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/config.py +0 -0
  78. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/exceptions.py +0 -0
  79. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/main.py +0 -0
  80. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/models.py +0 -0
  81. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/utils.py +0 -0
  82. {exist_shell-0.1.0 → exist_shell-0.1.2}/src/exist_shell/xquery.py +0 -0
  83. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/__init__.py +0 -0
  84. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/conftest.py +0 -0
  85. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_cache.py +0 -0
  86. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_client.py +0 -0
  87. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_cat.py +0 -0
  88. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_chmod.py +0 -0
  89. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_chown.py +0 -0
  90. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_collection.py +0 -0
  91. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_cp.py +0 -0
  92. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_edit.py +0 -0
  93. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_exec.py +0 -0
  94. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_group.py +0 -0
  95. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_ls.py +0 -0
  96. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_mkdir.py +0 -0
  97. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_mv.py +0 -0
  98. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_put.py +0 -0
  99. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_rm.py +0 -0
  100. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_server.py +0 -0
  101. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_sync.py +0 -0
  102. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_commands_user.py +0 -0
  103. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_completions.py +0 -0
  104. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_config.py +0 -0
  105. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_utils.py +0 -0
  106. {exist_shell-0.1.0 → exist_shell-0.1.2}/tests/test_xquery.py +0 -0
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## unreleased
4
4
 
5
+ ## 0.1.2 - 2026-06-23
6
+
7
+ ### Documentation
8
+
9
+ - Document installing `exsh` from PyPI (`uv tool install`, `pipx install`, `uvx --from`); the existing `git+https://...` instructions are kept as an alternative for tracking unreleased commits
10
+
11
+ ## 0.1.1 - 2026-06-23
12
+
13
+ ### Fixes
14
+
15
+ - Complete PyPI project metadata: `readme`, `license` (MIT), `authors`, `classifiers`, `project.urls`, and `keywords` were all missing, leaving the 0.1.0 PyPI listing with no description, license, or links
16
+ - Add `LICENSE` file (MIT)
17
+
5
18
  ## 0.1.0 - 2026-06-23
6
19
 
7
20
  ### Commands
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alberto Simões
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,280 @@
1
+ Metadata-Version: 2.4
2
+ Name: exist-shell
3
+ Version: 0.1.2
4
+ Summary: Command-line tool to interact with eXist-db via REST
5
+ Project-URL: Homepage, https://github.com/ambs/exist-shell
6
+ Project-URL: Repository, https://github.com/ambs/exist-shell
7
+ Project-URL: Documentation, https://ambs.github.io/exist-shell/
8
+ Project-URL: Issues, https://github.com/ambs/exist-shell/issues
9
+ Project-URL: Changelog, https://github.com/ambs/exist-shell/blob/main/CHANGELOG.md
10
+ Author-email: Alberto Simões <ambs@zbr.pt>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: cli,exist-db,xml,xquery
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Database
22
+ Classifier: Topic :: Software Development :: Libraries
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: httpx>=0.27
26
+ Requires-Dist: platformdirs>=4.10.0; sys_platform == 'win32'
27
+ Requires-Dist: pydantic>=2.13.4
28
+ Requires-Dist: tomlkit>=0.15.0
29
+ Requires-Dist: typer>=0.26.7
30
+ Description-Content-Type: text/markdown
31
+
32
+ # exsh — eXist-db Shell
33
+
34
+ [![Tests](https://github.com/ambs/exist-shell/actions/workflows/tests.yml/badge.svg)](https://github.com/ambs/exist-shell/actions/workflows/tests.yml)
35
+ [![Coverage](https://codecov.io/gh/ambs/exist-shell/graph/badge.svg)](https://codecov.io/gh/ambs/exist-shell)
36
+ [![e2e](https://github.com/ambs/exist-shell/actions/workflows/e2e.yml/badge.svg)](https://github.com/ambs/exist-shell/actions/workflows/e2e.yml)
37
+ [![Ruff](https://github.com/ambs/exist-shell/actions/workflows/ruff.yml/badge.svg)](https://github.com/ambs/exist-shell/actions/workflows/ruff.yml)
38
+ [![ty](https://github.com/ambs/exist-shell/actions/workflows/ty.yml/badge.svg)](https://github.com/ambs/exist-shell/actions/workflows/ty.yml)
39
+ [![docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://ambs.github.io/exist-shell/)
40
+
41
+ A command-line tool to interact with an [eXist-db](https://exist-db.org) server via its REST API. Designed for shell scripting and pipe-friendly workflows.
42
+
43
+ ## Requirements
44
+
45
+ - Python 3.11+
46
+ - [uv](https://docs.astral.sh/uv/)
47
+
48
+ ## Installation
49
+
50
+ Install system-wide with `uv tool`:
51
+
52
+ ```bash
53
+ uv tool install exist-shell
54
+ ```
55
+
56
+ Or with `pipx`:
57
+
58
+ ```bash
59
+ pipx install exist-shell
60
+ ```
61
+
62
+ This places `exsh` on your `PATH`. Verify with:
63
+
64
+ ```bash
65
+ exsh --version
66
+ ```
67
+
68
+ To uninstall:
69
+
70
+ ```bash
71
+ uv tool uninstall exist-shell
72
+ ```
73
+
74
+ ### Run without installing
75
+
76
+ ```bash
77
+ uvx --from exist-shell exsh --version
78
+ ```
79
+
80
+ `uvx` fetches the package into a temporary, cached environment and runs it — handy for one-off use or trying out a new release.
81
+
82
+ ### Install from git
83
+
84
+ To track an unreleased commit instead of a PyPI release:
85
+
86
+ ```bash
87
+ uv tool install git+https://github.com/ambs/exist-shell
88
+ ```
89
+
90
+ ## Configuration
91
+
92
+ ### Add a server
93
+
94
+ ```bash
95
+ exsh server add localhost --port 8080 --user admin
96
+ ```
97
+
98
+ A nickname is derived from the hostname by default (e.g. `localhost`). Override with `--nick`.
99
+
100
+ ### Add an existing collection
101
+
102
+ ```bash
103
+ exsh collection add mydata@localhost
104
+ ```
105
+
106
+ This registers the `/db/mydata` collection on the `localhost` server under the nick `mydata`.
107
+
108
+ ### Create and register a new collection
109
+
110
+ ```bash
111
+ exsh collection new mydata@localhost
112
+ ```
113
+
114
+ Creates `/db/mydata` on the server and registers it in one step. If the collection already exists, it prints a message and exits without modifying the config. Use `--nick` to register it under a different name:
115
+
116
+ ```bash
117
+ exsh collection new mydata@localhost --nick md
118
+ ```
119
+
120
+ List configured servers and collections:
121
+
122
+ ```bash
123
+ exsh server ls
124
+ exsh collection ls
125
+ ```
126
+
127
+ Configuration is stored at `~/.config/exsh/config.toml`.
128
+
129
+ ## Commands
130
+
131
+ | Command | Description |
132
+ |---------|-------------|
133
+ | `exsh ls <nick>[:<path>]` | List subcollections and documents at a path |
134
+ | `exsh cat <nick>:<path>` | Print a document to stdout |
135
+ | `exsh put <file> <nick>:<path>` | Upload a local file to a collection |
136
+ | `exsh cp <src> <dst>` | Copy a document (local ↔ remote or remote ↔ remote) |
137
+ | `exsh edit <nick>:<path>` | Open a document in `$EDITOR`, re-upload if changed |
138
+ | `exsh rm <nick>:<path>...` | Delete one or more documents |
139
+ | `exsh mkdir <nick>:<path>` | Create a collection |
140
+ | `exsh sync <local> <nick>[:<path>]` | Push a local folder to a remote collection |
141
+ | `exsh sync <nick>[:<path>] <local>` | Pull a remote collection to a local folder |
142
+ | `exsh exec <nick>[:<path>]` | Execute an XQuery script on a server |
143
+ | `exsh server add <host>` | Register a server |
144
+ | `exsh server ls` | List registered servers |
145
+ | `exsh server rm <nick>` | Remove a server (and its collections) |
146
+ | `exsh server rename <old> <new>` | Rename a server nick (updates collection references) |
147
+ | `exsh collection add <name>[@<server>]` | Register an existing collection |
148
+ | `exsh collection new <name>[@<server>]` | Create a collection on the server and register it |
149
+ | `exsh collection ls` | List registered collections |
150
+ | `exsh collection rm <nick>` | Remove a collection from the config |
151
+ | `exsh user ls [@server]` | List user accounts and their groups |
152
+ | `exsh user add <user[@server]>` | Create a user account (prompts for password) |
153
+ | `exsh user rm <user[@server]>` | Remove a user account |
154
+ | `exsh user info <user[@server]>` | Show user account details |
155
+ | `exsh group ls [@server]` | List groups and their members |
156
+ | `exsh group add <group[@server]>` | Create a group |
157
+ | `exsh group rm <group[@server]>` | Remove a group |
158
+ | `exsh chown <spec> <nick>:<path>` | Change owner and/or group of a document or collection |
159
+ | `exsh chmod <mode> <nick>:<path>` | Change POSIX permissions of a document or collection |
160
+
161
+ ### Examples
162
+
163
+ ```bash
164
+ # List the root of a collection
165
+ exsh ls mydata
166
+
167
+ # List a subdirectory
168
+ exsh ls mydata:reports/2025
169
+
170
+ # Print a document
171
+ exsh cat mydata:reports/2025/summary.xml
172
+
173
+ # Upload a file
174
+ exsh put report.xml mydata:reports/2025/report.xml
175
+
176
+ # Copy from remote to local
177
+ exsh cp mydata:reports/2025/report.xml ./report.xml
178
+
179
+ # Edit in place
180
+ exsh edit mydata:reports/2025/report.xml
181
+
182
+ # Delete a document
183
+ exsh rm mydata:reports/2025/old.xml
184
+
185
+ # Delete multiple documents
186
+ exsh rm mydata:reports/2025/a.xml mydata:reports/2025/b.xml
187
+
188
+ # Create a subcollection
189
+ exsh mkdir mydata:reports/2026
190
+
191
+ # Execute an XQuery script from a file
192
+ exsh exec mydata:/ -f query.xq
193
+
194
+ # Execute an XQuery script from stdin
195
+ echo 'count(collection("/db/mydata"))' | exsh exec mydata:/
196
+
197
+ # Execute without local preprocessing
198
+ exsh exec mydata:/ --no-fix -f query.xq
199
+
200
+ # List locally available XQuery validators
201
+ exsh exec --list-validators
202
+
203
+ # Push a local folder to the server (only transfers changed files)
204
+ exsh sync ./reports mydata:reports
205
+
206
+ # Pull a remote collection to a local folder
207
+ exsh sync mydata:reports ./reports
208
+
209
+ # Preview what would be transferred without doing it
210
+ exsh sync --dry-run ./reports mydata:reports
211
+
212
+ # Push and remove files on the server that no longer exist locally
213
+ exsh sync --delete ./reports mydata:reports
214
+
215
+ # Change owner and group of a document
216
+ exsh chown alice:editors mydata:reports/annual.xml
217
+
218
+ # Recursively reassign a collection tree
219
+ exsh chown -R alice mydata:reports
220
+
221
+ # Set permissions with octal mode
222
+ exsh chmod 0644 mydata:reports/annual.xml
223
+
224
+ # Add execute permission for the owner
225
+ exsh chmod u+x mydata:scripts/run.xq
226
+
227
+ # Recursively set permissions for a collection
228
+ exsh chmod -R 0644 mydata:data
229
+ ```
230
+
231
+ ## Sync
232
+
233
+ `exsh sync` transfers only files that have changed, using a local manifest stored at `~/.cache/exsh/sync/`. Direction is inferred from the argument order: local-first means push, remote-first means pull.
234
+
235
+ **Change detection:**
236
+ - **Push**: SHA-256 hash of the local file is compared against the manifest. Same-size edits are caught.
237
+ - **Pull**: `last_modified` timestamp from the eXist listing is compared against the manifest.
238
+
239
+ **Conflicts** (both sides changed since last sync) are reported and skipped — use `--force` to override.
240
+
241
+ **Options:**
242
+
243
+ | Flag | Effect |
244
+ |------|--------|
245
+ | `--force` / `-f` | Transfer all files, bypassing change detection |
246
+ | `--dry-run` / `-n` | Show what would happen without transferring |
247
+ | `--delete` | Remove files and empty folders on the destination that no longer exist on the source |
248
+ | `--verbose` / `-v` | Also print unchanged (skipped) files |
249
+ | `--checkpoint-every N` | Flush the manifest every N files (default: 100); allows interrupted syncs to resume near the point of failure |
250
+
251
+ ## Shell completion
252
+
253
+ Generate and install tab-completion for your shell:
254
+
255
+ ```bash
256
+ # bash
257
+ exsh --install-completion bash
258
+
259
+ # zsh
260
+ exsh --install-completion zsh
261
+
262
+ # fish
263
+ exsh --install-completion fish
264
+ ```
265
+
266
+ ## Development
267
+
268
+ ```bash
269
+ git clone https://github.com/ambs/exist-shell
270
+ cd exist-shell
271
+ uv sync
272
+ exsh --help
273
+ ```
274
+
275
+ Run checks:
276
+
277
+ ```bash
278
+ make checks # lint, type-check, and tests
279
+ make test # tests only
280
+ ```
@@ -19,7 +19,13 @@ A command-line tool to interact with an [eXist-db](https://exist-db.org) server
19
19
  Install system-wide with `uv tool`:
20
20
 
21
21
  ```bash
22
- uv tool install git+https://github.com/ambs/exist-shell
22
+ uv tool install exist-shell
23
+ ```
24
+
25
+ Or with `pipx`:
26
+
27
+ ```bash
28
+ pipx install exist-shell
23
29
  ```
24
30
 
25
31
  This places `exsh` on your `PATH`. Verify with:
@@ -34,6 +40,22 @@ To uninstall:
34
40
  uv tool uninstall exist-shell
35
41
  ```
36
42
 
43
+ ### Run without installing
44
+
45
+ ```bash
46
+ uvx --from exist-shell exsh --version
47
+ ```
48
+
49
+ `uvx` fetches the package into a temporary, cached environment and runs it — handy for one-off use or trying out a new release.
50
+
51
+ ### Install from git
52
+
53
+ To track an unreleased commit instead of a PyPI release:
54
+
55
+ ```bash
56
+ uv tool install git+https://github.com/ambs/exist-shell
57
+ ```
58
+
37
59
  ## Configuration
38
60
 
39
61
  ### Add a server
@@ -4,6 +4,14 @@
4
4
 
5
5
  ## Installation
6
6
 
7
+ ```bash
8
+ pip install exist-shell
9
+ # or with uv
10
+ uv add exist-shell
11
+ ```
12
+
13
+ To track an unreleased commit instead of a PyPI release:
14
+
7
15
  ```bash
8
16
  pip install git+https://github.com/ambs/exist-shell
9
17
  # or with uv
@@ -21,7 +21,7 @@
21
21
 
22
22
  ```bash
23
23
  # 1. Install
24
- uv tool install git+https://github.com/ambs/exist-shell
24
+ uv tool install exist-shell
25
25
 
26
26
  # 2. Register your server
27
27
  exsh server add localhost --port 8080 --user admin
@@ -7,10 +7,16 @@
7
7
 
8
8
  ## Install as a tool
9
9
 
10
- The recommended way is to install `exsh` as a uv tool so it is available system-wide:
10
+ The recommended way is to install `exsh` from PyPI as a uv tool so it is available system-wide:
11
11
 
12
12
  ```bash
13
- uv tool install git+https://github.com/ambs/exist-shell
13
+ uv tool install exist-shell
14
+ ```
15
+
16
+ Or with `pipx`:
17
+
18
+ ```bash
19
+ pipx install exist-shell
14
20
  ```
15
21
 
16
22
  Verify the installation:
@@ -19,6 +25,14 @@ Verify the installation:
19
25
  exsh --version
20
26
  ```
21
27
 
28
+ ## Run without installing
29
+
30
+ ```bash
31
+ uvx --from exist-shell exsh --version
32
+ ```
33
+
34
+ `uvx` runs the command in a temporary, cached environment without installing it permanently — useful for trying out `exsh` or running a one-off command.
35
+
22
36
  ## Upgrade
23
37
 
24
38
  ```bash
@@ -31,6 +45,14 @@ uv tool upgrade exist-shell
31
45
  uv tool uninstall exist-shell
32
46
  ```
33
47
 
48
+ ## Install from git
49
+
50
+ To install an unreleased commit instead of the latest PyPI release:
51
+
52
+ ```bash
53
+ uv tool install git+https://github.com/ambs/exist-shell
54
+ ```
55
+
34
56
  ## Install from a local clone
35
57
 
36
58
  If you want to develop or test from source:
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "exist-shell"
7
+ version = "0.1.2"
8
+ description = "Command-line tool to interact with eXist-db via REST"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [
12
+ { name = "Alberto Simões", email = "ambs@zbr.pt" },
13
+ ]
14
+ keywords = ["exist-db", "xml", "xquery", "cli"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ "Topic :: Database",
20
+ "Topic :: Software Development :: Libraries",
21
+ "Topic :: Utilities",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ ]
27
+ requires-python = ">=3.11"
28
+ dependencies = [
29
+ "typer>=0.26.7",
30
+ "httpx>=0.27",
31
+ "pydantic>=2.13.4",
32
+ "tomlkit>=0.15.0",
33
+ "platformdirs>=4.10.0; sys_platform == 'win32'",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/ambs/exist-shell"
38
+ Repository = "https://github.com/ambs/exist-shell"
39
+ Documentation = "https://ambs.github.io/exist-shell/"
40
+ Issues = "https://github.com/ambs/exist-shell/issues"
41
+ Changelog = "https://github.com/ambs/exist-shell/blob/main/CHANGELOG.md"
42
+
43
+ [project.scripts]
44
+ exsh = "exist_shell.main:app"
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ packages = ["src/exist_shell"]
48
+
49
+ [tool.ruff.lint]
50
+ select = ["D"]
51
+
52
+ [tool.ruff.lint.pydocstyle]
53
+ convention = "google"
54
+
55
+ [dependency-groups]
56
+ dev = [
57
+ "pytest>=8",
58
+ "pytest-cov>=5",
59
+ "pytest-httpx>=0.30",
60
+ "ruff>=0.15.16",
61
+ "ty>=0.0.46",
62
+ ]
@@ -157,7 +157,7 @@ toml = [
157
157
 
158
158
  [[package]]
159
159
  name = "exist-shell"
160
- version = "0.1.0"
160
+ version = "0.1.2"
161
161
  source = { editable = "." }
162
162
  dependencies = [
163
163
  { name = "httpx" },
@@ -1,10 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: exist-shell
3
- Version: 0.1.0
4
- Summary: Command-line tool to interact with eXist-db via REST
5
- Requires-Python: >=3.11
6
- Requires-Dist: httpx>=0.27
7
- Requires-Dist: platformdirs>=4.10.0; sys_platform == 'win32'
8
- Requires-Dist: pydantic>=2.13.4
9
- Requires-Dist: tomlkit>=0.15.0
10
- Requires-Dist: typer>=0.26.7
@@ -1,37 +0,0 @@
1
- [build-system]
2
- requires = ["hatchling"]
3
- build-backend = "hatchling.build"
4
-
5
- [project]
6
- name = "exist-shell"
7
- version = "0.1.0"
8
- description = "Command-line tool to interact with eXist-db via REST"
9
- requires-python = ">=3.11"
10
- dependencies = [
11
- "typer>=0.26.7",
12
- "httpx>=0.27",
13
- "pydantic>=2.13.4",
14
- "tomlkit>=0.15.0",
15
- "platformdirs>=4.10.0; sys_platform == 'win32'",
16
- ]
17
-
18
- [project.scripts]
19
- exsh = "exist_shell.main:app"
20
-
21
- [tool.hatch.build.targets.wheel]
22
- packages = ["src/exist_shell"]
23
-
24
- [tool.ruff.lint]
25
- select = ["D"]
26
-
27
- [tool.ruff.lint.pydocstyle]
28
- convention = "google"
29
-
30
- [dependency-groups]
31
- dev = [
32
- "pytest>=8",
33
- "pytest-cov>=5",
34
- "pytest-httpx>=0.30",
35
- "ruff>=0.15.16",
36
- "ty>=0.0.46",
37
- ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes