@codebend3r/che 1.2.0 → 1.2.2
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.
- package/README.md +259 -230
- package/bin/branchsync.py +324 -0
- package/bin/commands.py +17 -4
- package/bin/git/hard-reset-all-branches.py +250 -0
- package/bin/git/sync-all-branches.py +79 -72
- package/bin/gitutils.py +4 -4
- package/package.json +1 -1
- package/shell/che.bash +17 -4
- package/shell/che.fish +13 -3
- package/shell/che.zsh +17 -4
- package/shell/completions/_che +8 -4
- package/shell/completions/che.bash +5 -3
- package/shell/completions/che.fish +13 -3
package/README.md
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
# `che`
|
|
4
4
|
|
|
5
|
-
**
|
|
6
|
-
|
|
5
|
+
**Personal helper scripts, shared across machines, called by name from your
|
|
6
|
+
shell and browsable through one menu.**
|
|
7
7
|
|
|
8
8
|
[](https://github.com/codebend3r/che/actions/workflows/sanity-check.yml)
|
|
9
9
|
[](https://www.npmjs.com/package/@codebend3r/che)
|
|
10
10
|

|
|
11
11
|

|
|
12
12
|

|
|
13
|
+
[](LICENSE)
|
|
13
14
|
|
|
14
15
|
</div>
|
|
15
16
|
|
|
@@ -40,72 +41,104 @@ your shell, and browsable through one menu.**
|
|
|
40
41
|
<img src="menu.png" alt="The che menu: commands grouped by category on the left, details for the selected command on the right" width="850">
|
|
41
42
|
</div>
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
(`delete-by-ext`, `sync-all-branches`, `show-codecs`, …) are shell functions
|
|
45
|
-
generated from one manifest, `bin/commands.py`, and installed into whichever
|
|
46
|
-
shells you use.
|
|
44
|
+
<br>
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```
|
|
46
|
+
`che` is a set of standalone Python scripts for git housekeeping, media
|
|
47
|
+
libraries, file cleanup, NAS drives and system upkeep. Each one is installed
|
|
48
|
+
as a shell function in zsh, bash and fish, and all of them can be browsed,
|
|
49
|
+
filtered and run from a full-screen terminal menu.
|
|
53
50
|
|
|
54
51
|
<!-- che:count -->
|
|
55
|
-
|
|
52
|
+
34 commands across 5 categories, plus 6 built into `che` itself
|
|
56
53
|
<!-- /che:count -->
|
|
57
54
|
|
|
58
|
-
##
|
|
55
|
+
## Contents
|
|
56
|
+
|
|
57
|
+
- [Features](#features)
|
|
58
|
+
- [Quick start](#quick-start)
|
|
59
|
+
- [Installation](#installation)
|
|
60
|
+
- [Usage](#usage)
|
|
61
|
+
- [Commands](#commands)
|
|
62
|
+
- [Configuration](#configuration)
|
|
63
|
+
- [External tools](#external-tools)
|
|
64
|
+
- [Development](#development)
|
|
65
|
+
- [Troubleshooting](#troubleshooting)
|
|
66
|
+
- [License](#license)
|
|
67
|
+
|
|
68
|
+
## Features
|
|
59
69
|
|
|
60
70
|
- **One manifest, everything generated.** Wrappers, completions, the menu, the
|
|
61
|
-
doctor report and the tables in this README all come from
|
|
62
|
-
`
|
|
63
|
-
- **
|
|
64
|
-
that only prints
|
|
65
|
-
- **Standard library only.**
|
|
66
|
-
no `pip install
|
|
71
|
+
doctor report and the tables in this README all come from
|
|
72
|
+
`bin/commands.py`. CI fails if any of them drift.
|
|
73
|
+
- **Safe by default.** Destructive commands preview unless told otherwise, and
|
|
74
|
+
each has a `-dr` twin that only prints. The menu opens on the dry run.
|
|
75
|
+
- **Standard library only.** Any Python 3.12+ runs everything. No virtualenv,
|
|
76
|
+
no `pip install`.
|
|
67
77
|
- **Your dotfiles stay yours.** The installer writes a few lines between
|
|
68
|
-
markers, backs every rc file
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
blocks, external tools
|
|
78
|
+
markers, backs up every rc file first, and never touches anything outside
|
|
79
|
+
them.
|
|
80
|
+
- **Self-diagnosing.** `che doctor` checks the interpreter, generated files, rc
|
|
81
|
+
blocks, external tools and alias collisions, and exits non-zero on failure.
|
|
72
82
|
|
|
73
|
-
##
|
|
83
|
+
## Quick start
|
|
74
84
|
|
|
75
|
-
|
|
85
|
+
```sh
|
|
86
|
+
curl -fsSL https://raw.githubusercontent.com/codebend3r/che/main/install.sh | bash
|
|
87
|
+
exec $SHELL
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
che # browse everything: type to filter, Enter to run
|
|
92
|
+
che <command> [args…] # run one command directly
|
|
93
|
+
delete-by-ext --path=… # or call any command by name
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Installation
|
|
97
|
+
|
|
98
|
+
### Requirements
|
|
99
|
+
|
|
100
|
+
- **Python 3.12 or newer.** The installer picks the newest suitable interpreter
|
|
101
|
+
it can find and records its path, so an old system `python3` is fine as long
|
|
102
|
+
as a newer one exists (`brew install python@3.13` if not).
|
|
103
|
+
- **zsh, bash or fish.**
|
|
104
|
+
- Some commands need external tools such as `ffmpeg` or `gh`; see
|
|
105
|
+
[External tools](#external-tools).
|
|
106
|
+
|
|
107
|
+
### Install methods
|
|
108
|
+
|
|
109
|
+
**From a clone**
|
|
76
110
|
|
|
77
111
|
```sh
|
|
78
112
|
git clone https://github.com/codebend3r/che.git ~/Developer/git/che
|
|
79
113
|
~/Developer/git/che/install.sh
|
|
80
114
|
```
|
|
81
115
|
|
|
82
|
-
|
|
83
|
-
`CHE_HOME` points):
|
|
116
|
+
**One-liner** (clones into `~/Developer/git/che`, or `$CHE_HOME` if set)
|
|
84
117
|
|
|
85
118
|
```sh
|
|
86
119
|
curl -fsSL https://raw.githubusercontent.com/codebend3r/che/main/install.sh | bash
|
|
87
120
|
```
|
|
88
121
|
|
|
89
|
-
|
|
122
|
+
**From npm**
|
|
90
123
|
|
|
91
124
|
```sh
|
|
92
125
|
npm install -g @codebend3r/che
|
|
93
|
-
che
|
|
126
|
+
che # offers to install the shell wrappers on first run
|
|
94
127
|
```
|
|
95
128
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
129
|
+
> [!NOTE]
|
|
130
|
+
> Install the npm package globally rather than running it through `npx`: the
|
|
131
|
+
> wrappers point back at the package's location, and the `npx` cache is
|
|
132
|
+
> temporary. Update with `npm install -g @codebend3r/che@latest`; `che update`
|
|
133
|
+
> only works in a git clone.
|
|
134
|
+
|
|
135
|
+
### First run
|
|
100
136
|
|
|
101
|
-
`install.sh` finds a suitable
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
`brew install` them when Homebrew is present). A question already answered on
|
|
107
|
-
the command line (`--no-sounds`, `--no-path-shim`, `--replace-legacy`,
|
|
108
|
-
`--install-deps`) is not asked again.
|
|
137
|
+
`install.sh` finds a suitable Python and hands over to a short wizard. It
|
|
138
|
+
lists the shells it found and the rc files it would touch, asks whether to put
|
|
139
|
+
`che` on `PATH` and whether to keep the completion chimes, offers to remove
|
|
140
|
+
hand-written wrappers from before the installer existed, and reports missing
|
|
141
|
+
external tools (offering to `brew install` them when Homebrew is present).
|
|
109
142
|
|
|
110
143
|
<div align="center">
|
|
111
144
|
<img src="first-run.png" alt="The first run screen: che is not installed in this shell yet, offering to install, open the menu, or quit" width="700">
|
|
@@ -113,29 +146,31 @@ the command line (`--no-sounds`, `--no-path-shim`, `--replace-legacy`,
|
|
|
113
146
|
<em>Running <code>che</code> before installing offers to set itself up.</em>
|
|
114
147
|
</div>
|
|
115
148
|
|
|
116
|
-
|
|
149
|
+
### Unattended install
|
|
150
|
+
|
|
151
|
+
Any question answered on the command line is not asked again
|
|
152
|
+
(`--no-sounds`, `--no-path-shim`, `--replace-legacy`, `--install-deps`).
|
|
117
153
|
|
|
118
154
|
```sh
|
|
119
|
-
./install.sh --yes
|
|
120
|
-
./install.sh --yes --shells=zsh,fish
|
|
121
|
-
./install.sh --dry-run
|
|
122
|
-
./install.sh --yes --replace-legacy
|
|
123
|
-
./install.sh --print
|
|
155
|
+
./install.sh --yes # install into every detected shell
|
|
156
|
+
./install.sh --yes --shells=zsh,fish # or only the shells you name
|
|
157
|
+
./install.sh --dry-run # report what would change, write nothing
|
|
158
|
+
./install.sh --yes --replace-legacy # also remove pre-che hand-written wrappers
|
|
159
|
+
./install.sh --print # print the rc block and exit
|
|
124
160
|
```
|
|
125
161
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
### What lands where
|
|
162
|
+
### What gets installed
|
|
129
163
|
|
|
130
|
-
|
|
|
164
|
+
| What | Where |
|
|
131
165
|
| --- | --- |
|
|
132
|
-
| The rc block
|
|
133
|
-
|
|
|
134
|
-
| Tab completions | `shell/completions/`, sourced by the
|
|
135
|
-
| A `che` executable
|
|
136
|
-
|
|
|
137
|
-
|
|
138
|
-
|
|
166
|
+
| The rc block | `~/.zshrc` (or `$ZDOTDIR/.zshrc`), `~/.bashrc` and `~/.bash_profile` (whichever exist), `~/.config/fish/config.fish`, and `~/.profile` only when `sh` is requested |
|
|
167
|
+
| Shell wrappers | `shell/che.zsh`, `shell/che.bash`, `shell/che.fish` in the repo |
|
|
168
|
+
| Tab completions | `shell/completions/`, sourced by the wrapper file |
|
|
169
|
+
| A `che` executable for cron and scripts | `~/.local/bin/che` |
|
|
170
|
+
| Your installer answers | `~/.config/che/config.json` |
|
|
171
|
+
| rc file backups | `~/.config/che/backups/` |
|
|
172
|
+
|
|
173
|
+
The rc block is deliberately small, so upgrading `che` never rewrites your
|
|
139
174
|
dotfiles:
|
|
140
175
|
|
|
141
176
|
```zsh
|
|
@@ -148,37 +183,28 @@ case ":$PATH:" in *":/Users/you/.local/bin:"*) ;; *) PATH="/Users/you/.local/bin
|
|
|
148
183
|
# <<< che shell functions <<<
|
|
149
184
|
```
|
|
150
185
|
|
|
151
|
-
Everything between
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Then reload:
|
|
155
|
-
|
|
156
|
-
```sh
|
|
157
|
-
exec zsh # or exec bash / exec fish
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
**Requires Python 3.12+** and nothing else to run the scripts. The installer
|
|
161
|
-
picks the newest suitable interpreter it can find and records its path in the
|
|
162
|
-
block, so an old `/usr/bin/python3` does not matter as long as some newer
|
|
163
|
-
python3 exists. `brew install python@3.13` if none does.
|
|
186
|
+
Everything between the markers is rewritten on every install; everything
|
|
187
|
+
outside them is left alone. Reload your shell afterwards with `exec zsh`
|
|
188
|
+
(or `exec bash`, `exec fish`).
|
|
164
189
|
|
|
165
|
-
###
|
|
190
|
+
### Updating and uninstalling
|
|
166
191
|
|
|
167
192
|
```sh
|
|
168
|
-
che
|
|
193
|
+
che update # pull the latest scripts and refresh the wrappers
|
|
194
|
+
che uninstall # remove the rc block, the shim and the completions
|
|
169
195
|
che uninstall --purge # also delete ~/.config/che and the menu's input history
|
|
170
196
|
```
|
|
171
197
|
|
|
172
|
-
|
|
198
|
+
Uninstalling leaves the repository in place; delete the directory to finish.
|
|
173
199
|
|
|
174
|
-
##
|
|
200
|
+
## Usage
|
|
175
201
|
|
|
176
|
-
|
|
177
|
-
default**, so pressing Enter on `delete-by-ext` shows you what it would delete
|
|
178
|
-
rather than deleting it.
|
|
202
|
+
### The menu
|
|
179
203
|
|
|
180
|
-
|
|
181
|
-
the selected command's summary, script, required tools and flags
|
|
204
|
+
Run `che` with no arguments to open the menu. Type to filter; the right pane
|
|
205
|
+
shows the selected command's summary, script, required tools and flags.
|
|
206
|
+
Anything that can preview **previews by default**, so pressing Enter on
|
|
207
|
+
`delete-by-ext` shows what it would delete rather than deleting it.
|
|
182
208
|
|
|
183
209
|
<div align="center">
|
|
184
210
|
<img src="search.png" alt="The menu filtered by the search term branch, showing the five matching commands" width="850">
|
|
@@ -198,19 +224,28 @@ the selected command's summary, script, required tools and flags:
|
|
|
198
224
|
| `q` | quit |
|
|
199
225
|
<!-- /che:keys -->
|
|
200
226
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
227
|
+
### From the shell
|
|
228
|
+
|
|
229
|
+
Every command is a plain shell function, so `type delete-by-ext` shows exactly
|
|
230
|
+
what it runs. Destructive commands have a `-dr` twin that only prints:
|
|
204
231
|
|
|
205
232
|
```sh
|
|
206
233
|
delete-by-ext --path=/Volumes/Media --ext=nfo,txt # deletes
|
|
207
234
|
delete-by-ext-dr --path=/Volumes/Media --ext=nfo # shows what it would delete
|
|
208
235
|
```
|
|
209
236
|
|
|
210
|
-
`che <command>` runs the same
|
|
211
|
-
|
|
237
|
+
`che <command>` runs the same script with the same environment its wrapper
|
|
238
|
+
uses, and `che help <command>` prints that script's `--help`.
|
|
239
|
+
|
|
240
|
+
All scripts share the same CLI conventions:
|
|
241
|
+
|
|
242
|
+
- **Long flags only**, with `=` for values: `--path=/foo`, `--ext=nfo,txt`.
|
|
243
|
+
- **Booleans** accept a bare `--flag` or `--flag=true|false` (also
|
|
244
|
+
`yes|no`, `1|0`, `on|off`). A bare flag never swallows the next argument.
|
|
245
|
+
- **`--help`** on every script.
|
|
246
|
+
- **A symlinked `--path` is refused.** Pass the real path to act on it.
|
|
212
247
|
|
|
213
|
-
|
|
248
|
+
## Commands
|
|
214
249
|
|
|
215
250
|
<!-- che:commands -->
|
|
216
251
|
| Command | Does | Preview |
|
|
@@ -220,8 +255,9 @@ have used, and `che help <command>` prints that script's own `--help`.
|
|
|
220
255
|
| `all-actions-watch` | Same as all-actions, refreshed on an interval | |
|
|
221
256
|
| `checkout-my-branches` | Check out recent remote branches you authored that aren't local yet | |
|
|
222
257
|
| `clean-stale-branches` | Delete local branches whose upstream is gone | `clean-stale-branches-dr` |
|
|
258
|
+
| `hard-reset-all-branches` | Check out every local branch and hard reset it to origin/<branch> | `hard-reset-all-branches-dr` |
|
|
223
259
|
| `prune-worktrees` | Remove every linked worktree, then prune stale admin records | `prune-worktrees-dr` |
|
|
224
|
-
| `sync-all-branches` |
|
|
260
|
+
| `sync-all-branches` | Update every branch and worktree, preserving locks and local work | `sync-all-branches-dr` |
|
|
225
261
|
| `update-from-origin` | Alias of sync-all-branches | `--dry-run` |
|
|
226
262
|
| `update-local-branches` | Rebase every local branch that has an upstream onto origin | `--dry-run` |
|
|
227
263
|
| **Video** | *Codecs, renaming, dedupe and metadata* | |
|
|
@@ -255,12 +291,11 @@ have used, and `che help <command>` prints that script's own `--help`.
|
|
|
255
291
|
| `btop` | Launch btop with a gruvbox theme matching the macOS appearance | |
|
|
256
292
|
<!-- /che:commands -->
|
|
257
293
|
|
|
258
|
-
Two commands have no `-dr` twin. `remove-metadata` cannot preview
|
|
259
|
-
exiftool rewrites in place. `rename-video-file`
|
|
260
|
-
|
|
261
|
-
`--dry-run=false`.
|
|
294
|
+
Two commands have no `-dr` twin. `remove-metadata` cannot preview, since
|
|
295
|
+
exiftool rewrites files in place. `rename-video-file` already defaults to
|
|
296
|
+
`--dry-run=true`, so the real rename is the one that needs `--dry-run=false`.
|
|
262
297
|
|
|
263
|
-
###
|
|
298
|
+
### Built-in commands
|
|
264
299
|
|
|
265
300
|
<!-- che:builtins -->
|
|
266
301
|
| Command | Does |
|
|
@@ -273,10 +308,11 @@ defaults to `--dry-run=true`, so the real rename is the one that needs
|
|
|
273
308
|
| `che completions` | Print the completion script for a shell |
|
|
274
309
|
<!-- /che:builtins -->
|
|
275
310
|
|
|
276
|
-
`che doctor` checks the interpreter, the
|
|
277
|
-
current, whether each rc file has the block, whether `che` is on `PATH`,
|
|
278
|
-
external tool, and anything shadowing a wrapper. It exits non-zero on a
|
|
279
|
-
failure, so it
|
|
311
|
+
`che doctor` checks the interpreter, the repository, whether generated files
|
|
312
|
+
are current, whether each rc file has the block, whether `che` is on `PATH`,
|
|
313
|
+
every external tool, and anything shadowing a wrapper. It exits non-zero on a
|
|
314
|
+
real failure, so it can serve as a health check; `che doctor --json` gives
|
|
315
|
+
machine-readable output.
|
|
280
316
|
|
|
281
317
|
<div align="center">
|
|
282
318
|
<img src="doctor.png" alt="che doctor output: green checks for python, repo and generated files, with problems and warnings each followed by the command that fixes them" width="850">
|
|
@@ -284,109 +320,50 @@ failure, so it works in a health check. `che doctor --json` for a machine.
|
|
|
284
320
|
<em>Every problem <code>che doctor</code> reports comes with the command that fixes it.</em>
|
|
285
321
|
</div>
|
|
286
322
|
|
|
287
|
-
###
|
|
323
|
+
### Syncing branches
|
|
288
324
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
| `CHE_HOME` | Where this repo lives. Set by the block; the wrapper file falls back to its own location. |
|
|
292
|
-
| `CHE_PYTHON` | Interpreter the wrappers use. Set by the block to the one the installer verified. |
|
|
293
|
-
| `CHE_SOUNDS` | `0` turns off the `playsound-N` chime after every command. |
|
|
294
|
-
| `DRY_RUN` | What the wrappers set for the destructive commands. Exporting it yourself changes nothing for them, since the wrapper sets it explicitly on every call; it only reaches a script invoked directly. |
|
|
295
|
-
| `NO_COLOR` | Honoured by every script and by the menu. |
|
|
296
|
-
|
|
297
|
-
## How it fits together
|
|
298
|
-
|
|
299
|
-
`bin/commands.py` is the single source of truth. The wrappers, the completions,
|
|
300
|
-
the menu, the `doctor` report and the tables in this README are all generated
|
|
301
|
-
from it, and `bun run ci` fails if any of them drift.
|
|
302
|
-
|
|
303
|
-
### Adding a command
|
|
304
|
-
|
|
305
|
-
1. Write the script under `bin/<category>/` and `chmod +x` it.
|
|
306
|
-
2. Add a `Command(...)` to `bin/commands.py`: name, summary, script, sound,
|
|
307
|
-
whether it can preview, which external tools it needs, its flags, and the
|
|
308
|
-
two or three questions the menu should ask.
|
|
309
|
-
3. `bun run generate` to rewrite `shell/` and this README's tables, and commit
|
|
310
|
-
the result.
|
|
311
|
-
4. `che install` on each machine, or `che update`, which does both.
|
|
312
|
-
|
|
313
|
-
There is no step where you hand-edit a wrapper into `~/.zshrc`, and no step
|
|
314
|
-
where you copy one machine's rc file to another.
|
|
315
|
-
|
|
316
|
-
### Layout
|
|
317
|
-
|
|
318
|
-
```
|
|
319
|
-
bin/
|
|
320
|
-
art.py the wordmark and the face beside it, for every screen
|
|
321
|
-
che.py the dispatcher, and the loop that runs what the menu picks
|
|
322
|
-
commands.py the command manifest: the source of truth for everything
|
|
323
|
-
doctor.py `che doctor`: every check on the install
|
|
324
|
-
gitutils.py shared git helpers: worktrees, main branch, hook skipping
|
|
325
|
-
install.py installer, first-run wizard, self-update, uninstall
|
|
326
|
-
machine.py shells, rc file paths, python, config: facts about the machine
|
|
327
|
-
media.py shared ffprobe helpers for the video scripts
|
|
328
|
-
menu.py the menu's list, keys and drawing
|
|
329
|
-
nas.py the NAS drive table and SMB mounting, for the drive scripts
|
|
330
|
-
rcfile.py edits to rc files: the che block and old hand-written wrappers
|
|
331
|
-
shellgen.py renders the wrappers, completions and README tables
|
|
332
|
-
tui.py terminal primitives: raw input, frame buffer, prompts
|
|
333
|
-
utils.py shared library: logging, arg parsing, subprocess, files
|
|
334
|
-
drives/ mount, eject, and keep-alive for the NAS volumes
|
|
335
|
-
files/ delete/compress/inspect files by extension or size
|
|
336
|
-
git/ branch hygiene, worktrees, GitHub Actions status
|
|
337
|
-
system/ Homebrew updates, btop launcher
|
|
338
|
-
video/ codec inspection, renaming, dedupe, metadata
|
|
339
|
-
shell/ GENERATED wrappers and completions, do not edit
|
|
340
|
-
test/ pytest suites
|
|
341
|
-
install.sh bootstrap for a machine with nothing on it
|
|
342
|
-
.zshrc a shim that sources shell/che.zsh, kept for old setups
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
## CLI conventions
|
|
346
|
-
|
|
347
|
-
Every script follows the same shape, described in full at the top of
|
|
348
|
-
`bin/utils.py`:
|
|
349
|
-
|
|
350
|
-
- **Long flags only**, `--name=value` for values.
|
|
351
|
-
- **Booleans** accept a bare `--flag` *and* `--flag=true|false` (plus
|
|
352
|
-
`yes|no`, `1|0`, `on|off`). A bare `--flag` never swallows the next argument.
|
|
353
|
-
- **`--help`** on every script.
|
|
354
|
-
- **Destructive scripts default to dry-run.** `delete-by-ext`,
|
|
355
|
-
`delete-empty-folders`, `delete-smb-files`, `files-under-size`, `fix-codecs`,
|
|
356
|
-
`delete-duplicate-videos` and `prune-worktrees` read `DRY_RUN` and preview
|
|
357
|
-
unless told otherwise; the generated wrapper sets `DRY_RUN=false` for the
|
|
358
|
-
real thing and the `-dr` twin sets it back to `true`.
|
|
359
|
-
- **A symlinked `--path` is refused.** `find` never followed a symlinked start
|
|
360
|
-
point, so pointing a delete tool at `~/media -> /Volumes/...` used to be a
|
|
361
|
-
silent no-op. Pass the real path to act on it.
|
|
362
|
-
|
|
363
|
-
## Syncing branches
|
|
325
|
+
`sync-all-branches` (alias `update-from-origin`) fetches once and rebases
|
|
326
|
+
every local branch onto its upstream, falling back to `origin/<branch>`.
|
|
364
327
|
|
|
365
328
|
```sh
|
|
366
|
-
sync-all-branches #
|
|
367
|
-
sync-all-branches-dr # preview
|
|
368
|
-
update-from-origin # alias of sync-all-branches
|
|
329
|
+
sync-all-branches # update every branch and worktree
|
|
330
|
+
sync-all-branches-dr # preview against cached remote refs; no fetch, no changes
|
|
369
331
|
```
|
|
370
332
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
333
|
+
- **Worktrees update in place.** Branches checked out in other worktrees,
|
|
334
|
+
including locked ones, rebase inside their own checkout. Unoccupied branches
|
|
335
|
+
use temporary worktrees, so your current checkout never switches.
|
|
336
|
+
- **Local work is preserved.** Uncommitted tracked and untracked changes are
|
|
337
|
+
stashed and restored with their staged state; existing stashes are
|
|
338
|
+
untouched. Dirty submodules and in-progress Git operations are reported and
|
|
339
|
+
left alone.
|
|
340
|
+
- **Nothing is destroyed.** It never pushes, hard resets or deletes branches.
|
|
341
|
+
Missing upstreams are reported, not treated as merged; use
|
|
342
|
+
`clean-stale-branches` for cleanup.
|
|
343
|
+
- **Failures are isolated.** A rebase conflict is aborted and reported while
|
|
344
|
+
other branches continue, and any blocked branch makes the command exit
|
|
345
|
+
non-zero. If restoring work conflicts, the recovery stash ID and worktree
|
|
346
|
+
path are printed.
|
|
347
|
+
- New remote branches you authored become local tracking branches.
|
|
348
|
+
|
|
349
|
+
Like every branch-touching script in `bin/git/`, it disables repository hooks
|
|
350
|
+
for its own Git commands. The legacy `--no-push` and `--keep-locked` flags are
|
|
351
|
+
still accepted and ignored.
|
|
352
|
+
|
|
353
|
+
## Configuration
|
|
375
354
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
post-checkout never fires on a checkout or a rebase.
|
|
355
|
+
| Variable | Effect |
|
|
356
|
+
| --- | --- |
|
|
357
|
+
| `CHE_HOME` | Where the repository lives. Set by the rc block; the wrapper file falls back to its own location. |
|
|
358
|
+
| `CHE_PYTHON` | The interpreter the wrappers use. Set by the rc block to the one the installer verified. |
|
|
359
|
+
| `CHE_SOUNDS` | Set to `0` to turn off the `playsound-N` chime after each command. |
|
|
360
|
+
| `DRY_RUN` | Set explicitly by the wrappers on every call, so exporting it only affects scripts run directly. |
|
|
361
|
+
| `NO_COLOR` | Disables color in every script and in the menu. |
|
|
384
362
|
|
|
385
363
|
## External tools
|
|
386
364
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
them.
|
|
365
|
+
Scripts that shell out to an external tool fail with an install hint when it
|
|
366
|
+
is missing, and `che doctor` reports which are present.
|
|
390
367
|
|
|
391
368
|
<!-- che:tools -->
|
|
392
369
|
| Tool | Needed by |
|
|
@@ -398,67 +375,119 @@ them.
|
|
|
398
375
|
| `ffmpeg` | `fix-codecs` |
|
|
399
376
|
| `ffprobe` | `show-codecs`, `find-video-mkv-issues`, `scan-videos-audio-language` |
|
|
400
377
|
| `gh` | `all-actions`, `all-actions-watch` |
|
|
401
|
-
| `git` | `all-actions`, `all-actions-watch`, `checkout-my-branches`, `clean-stale-branches`, `prune-worktrees`, `sync-all-branches`, `update-from-origin`, `update-local-branches`, `update` |
|
|
378
|
+
| `git` | `all-actions`, `all-actions-watch`, `checkout-my-branches`, `clean-stale-branches`, `hard-reset-all-branches`, `prune-worktrees`, `sync-all-branches`, `update-from-origin`, `update-local-branches`, `update` |
|
|
402
379
|
| `mpv` | `validate-video-files` |
|
|
403
380
|
| `osascript` | `mount-all-drives` |
|
|
404
381
|
| (not a binary) | `detect-green-magenta-videos` needs OpenCV in ~/.venvs/green-magenta (override with $DETECT_GM_PYTHON) |
|
|
405
382
|
<!-- /che:tools -->
|
|
406
383
|
|
|
407
|
-
`diskutil` and `osascript` ship with macOS. The rest are
|
|
408
|
-
away, and `che install --install-deps`
|
|
384
|
+
`diskutil` and `osascript` ship with macOS. The rest are a `brew install`
|
|
385
|
+
away, and `che install --install-deps` installs them for you.
|
|
409
386
|
|
|
410
|
-
`playsound-N`, the chime a wrapper
|
|
411
|
-
this
|
|
412
|
-
turns the chimes off everywhere.
|
|
387
|
+
`playsound-N`, the chime played when a wrapper finishes, is defined outside
|
|
388
|
+
this repository; the wrappers skip it silently when it is absent.
|
|
413
389
|
|
|
414
|
-
|
|
415
|
-
integer maths, `json`, `re` and `sorted()` cover what they were doing.
|
|
390
|
+
## Development
|
|
416
391
|
|
|
417
|
-
|
|
392
|
+
`bin/commands.py` is the single source of truth. The wrappers, completions,
|
|
393
|
+
menu, doctor report and this README's tables are all generated from it.
|
|
418
394
|
|
|
419
|
-
|
|
420
|
-
|
|
395
|
+
### Adding a command
|
|
396
|
+
|
|
397
|
+
1. Write the script under `bin/<category>/` and `chmod +x` it.
|
|
398
|
+
2. Add a `Command(...)` to `bin/commands.py`: name, summary, script, sound,
|
|
399
|
+
preview style, required tools, flags, and the two or three questions the
|
|
400
|
+
menu should ask.
|
|
401
|
+
3. Run `bun run generate` to rewrite `shell/` and the README tables, and
|
|
402
|
+
commit the result.
|
|
403
|
+
4. Run `che update` on each machine.
|
|
404
|
+
|
|
405
|
+
Conventions for new scripts are documented at the top of `bin/utils.py` and in
|
|
406
|
+
[`CLAUDE.md`](CLAUDE.md).
|
|
407
|
+
|
|
408
|
+
### Project layout
|
|
421
409
|
|
|
422
|
-
```sh
|
|
423
|
-
bun run test # pytest
|
|
424
|
-
bun run generate # rewrite shell/ and this README's tables from the manifest
|
|
425
|
-
bun run ci # ruff + exec bits + shebangs + pytest (stale generated files fail it)
|
|
426
410
|
```
|
|
411
|
+
bin/
|
|
412
|
+
che.py dispatcher, and the loop that runs what the menu picks
|
|
413
|
+
commands.py command manifest: the source of truth for everything
|
|
414
|
+
menu.py the menu's model, keys and drawing
|
|
415
|
+
shellgen.py renders wrappers, completions and README tables
|
|
416
|
+
install.py installer, first-run wizard, self-update, uninstall
|
|
417
|
+
doctor.py `che doctor`
|
|
418
|
+
rcfile.py rc file edits: the che block and legacy wrappers
|
|
419
|
+
machine.py shells, rc paths, python discovery, install record
|
|
420
|
+
tui.py terminal primitives: raw input, frame buffer, prompts
|
|
421
|
+
art.py the wordmark and banner
|
|
422
|
+
utils.py shared library: logging, arg parsing, subprocess, files
|
|
423
|
+
gitutils.py git helpers: worktrees, main branch, hook skipping
|
|
424
|
+
branchsync.py per-branch sync used by sync-all-branches
|
|
425
|
+
media.py ffprobe helpers for the video scripts
|
|
426
|
+
nas.py NAS drive table and SMB mounting
|
|
427
|
+
git/ video/ files/ drives/ system/ the scripts, by category
|
|
428
|
+
shell/ GENERATED wrappers and completions (do not edit)
|
|
429
|
+
test/ pytest suites
|
|
430
|
+
install.sh bootstrap for a bare machine
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Testing
|
|
427
434
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
435
|
+
The repository uses [Bun](https://bun.sh) as its task runner and
|
|
436
|
+
[uv](https://docs.astral.sh/uv/) for dev tooling, so neither `pytest` nor
|
|
437
|
+
`ruff` needs to be installed globally. Versions are pinned in
|
|
431
438
|
`requirements-dev.txt`.
|
|
432
439
|
|
|
433
|
-
|
|
434
|
-
|
|
440
|
+
```sh
|
|
441
|
+
bun run test # pytest
|
|
442
|
+
bun run generate # rewrite shell/ and the README tables from the manifest
|
|
443
|
+
bun run ci # ruff, exec bits, shebangs, pytest; fails on stale generated files
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
> [!TIP]
|
|
447
|
+
> Use `bun run test`, not `bun test`, which runs Bun's built-in JavaScript
|
|
448
|
+
> test runner instead.
|
|
435
449
|
|
|
436
450
|
## Troubleshooting
|
|
437
451
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
alias back, so your alias keeps winning at the prompt exactly as before; run
|
|
441
|
-
`\name` (backslash) to reach the wrapper, or remove the alias. `che doctor`
|
|
442
|
-
lists every collision it finds.
|
|
452
|
+
<details>
|
|
453
|
+
<summary><strong>A command still runs the old version</strong></summary>
|
|
443
454
|
|
|
444
|
-
|
|
445
|
-
|
|
455
|
+
An alias beats a function in zsh and bash. The wrappers preserve any colliding
|
|
456
|
+
alias, so it keeps winning at the prompt. Run `\name` to reach the wrapper, or
|
|
457
|
+
remove the alias. `che doctor` lists every collision.
|
|
446
458
|
|
|
447
|
-
|
|
448
|
-
reinstalls; `che doctor` says whether `shell/` is stale or the block is out of
|
|
449
|
-
date.
|
|
459
|
+
</details>
|
|
450
460
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
461
|
+
<details>
|
|
462
|
+
<summary><strong><code>che: command not found</code> in a script or cron job</strong></summary>
|
|
463
|
+
|
|
464
|
+
Non-interactive shells never read an rc file. Use `~/.local/bin/che`, which
|
|
465
|
+
the installer puts on `PATH`.
|
|
466
|
+
|
|
467
|
+
</details>
|
|
468
|
+
|
|
469
|
+
<details>
|
|
470
|
+
<summary><strong>A wrapper is missing after an update</strong></summary>
|
|
471
|
+
|
|
472
|
+
`che update` regenerates and reinstalls the wrappers. `che doctor` reports
|
|
473
|
+
whether `shell/` is stale or the rc block is out of date.
|
|
474
|
+
|
|
475
|
+
</details>
|
|
476
|
+
|
|
477
|
+
<details>
|
|
478
|
+
<summary><strong>Old hand-written wrappers are still in an rc file</strong></summary>
|
|
479
|
+
|
|
480
|
+
`che doctor` counts them, and `che install --replace-legacy` removes them,
|
|
481
|
+
along with their helpers and the lines that called them, after a backup.
|
|
482
|
+
|
|
483
|
+
</details>
|
|
454
484
|
|
|
455
485
|
## History
|
|
456
486
|
|
|
457
|
-
These scripts were bash until August 2026
|
|
458
|
-
|
|
459
|
-
the
|
|
460
|
-
|
|
487
|
+
These scripts were written in bash until August 2026, when they were ported to
|
|
488
|
+
Python. Every intentional behavioural difference from the bash originals is
|
|
489
|
+
commented at the line where it occurs, and `git log` holds the shell versions.
|
|
490
|
+
|
|
491
|
+
## License
|
|
461
492
|
|
|
462
|
-
|
|
463
|
-
with the documented workflow being to copy them into `~/.zshrc` by hand. That
|
|
464
|
-
is what `che install --replace-legacy` cleans up.
|
|
493
|
+
[MIT](LICENSE) © CJ Rivas
|