workforest 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.
- {workforest-0.3.0 → workforest-0.4.0}/PKG-INFO +77 -24
- {workforest-0.3.0 → workforest-0.4.0}/README.md +76 -23
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/__init__.py +1 -1
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/cli.py +8 -1
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/commands.py +4 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/completions.py +4 -4
- workforest-0.4.0/src/workforest/config.py +313 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/examples/config.yaml +78 -47
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/launch.py +99 -49
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_cli.py +12 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_config.py +158 -29
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_launch.py +187 -58
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_shell.py +11 -4
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_tui.py +13 -3
- workforest-0.3.0/src/workforest/config.py +0 -200
- {workforest-0.3.0 → workforest-0.4.0}/.claude/rules/architecture.md +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/.claude/rules/conventions.md +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/.claude/rules/tests.md +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/.github/workflows/ci.yml +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/.github/workflows/publish_aur.yml +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/.github/workflows/publish_homebrew.yml +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/.github/workflows/publish_pypi.yml +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/.github/workflows/release.yml +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/.gitignore +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/LICENSE +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/Makefile +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/completions/_workforest +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/packaging/AUR/PKGBUILD.template +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/packaging/homebrew/workforest.rb.template +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/pyproject.toml +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/__main__.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/errors.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/gitutil.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/hooks.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/integrations/__init__.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/integrations/claude.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/output.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/shell/completion.bash +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/shell/completion.zsh +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/shell/workforest.sh +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/shellinit.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/src/workforest/tui.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/__init__.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/conftest.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_claude.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_commands.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_gitutil.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_harness.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_hooks.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/tests/test_output.py +0 -0
- {workforest-0.3.0 → workforest-0.4.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: workforest
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Git worktree forest management: create, open, and clean up per-branch worktrees with project-defined setup hooks
|
|
5
5
|
Project-URL: Homepage, https://github.com/ArkadyBuryakov/workforest
|
|
6
6
|
Author-email: Arkady Buryakov <arkady@buryakov.pro>
|
|
@@ -44,8 +44,8 @@ opened, and cleaned up with one command.
|
|
|
44
44
|
- **Create** a worktree for any branch (local, remote, or brand new) and have
|
|
45
45
|
it set up automatically: symlinks for untracked assets (`node_modules`,
|
|
46
46
|
`.env`, …) and project-defined setup scripts.
|
|
47
|
-
- **Open** it in your editor — in
|
|
48
|
-
|
|
47
|
+
- **Open** it in your editor — in your terminal, in a new terminal window or
|
|
48
|
+
multiplexer pane, or in a GUI app, all from a small shell-command config.
|
|
49
49
|
- **Run** named project scripts with well-known `WF_*` environment variables.
|
|
50
50
|
- **Delete** worktrees safely, or **checkout**: collapse one back into the
|
|
51
51
|
main checkout.
|
|
@@ -106,27 +106,77 @@ Layered, YAML or JSON; later layers override earlier ones:
|
|
|
106
106
|
| project (shared) | `.workforest.yaml` in the repo root | repo policy, committed |
|
|
107
107
|
| project (local) | `.vscode/` or `.idea/` `.workforest.yaml` | personal overrides, untracked |
|
|
108
108
|
|
|
109
|
-
Scalars and lists replace; the `scripts
|
|
110
|
-
(`null` removes an entry). `workforest config` shows the
|
|
111
|
-
where each layer came from; `workforest init` scaffolds a
|
|
112
|
-
(`--local` for a personal one).
|
|
109
|
+
Scalars and lists replace; the `openers`/`wrappers`/`scripts` mappings
|
|
110
|
+
merge per key (`null` removes an entry). `workforest config` shows the
|
|
111
|
+
merged result and where each layer came from; `workforest init` scaffolds a
|
|
112
|
+
project file (`--local` for a personal one). Nothing in the environment
|
|
113
|
+
changes the result — files and flags only.
|
|
113
114
|
|
|
114
115
|
All keys, with defaults:
|
|
115
116
|
|
|
116
117
|
```yaml
|
|
117
118
|
worktrees_dir: "$WF_MAIN/../worktrees/$WF_NAME" # where the forest lives
|
|
118
|
-
opener: "" # default opener
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
opener: "" # default opener: an `openers` name or a shell command;
|
|
120
|
+
# "" → $VISUAL → $EDITOR
|
|
121
|
+
openers: {} # name -> what `-o NAME` runs, and where
|
|
122
|
+
wrappers: {} # name -> command that runs $WF_COMMAND elsewhere (window, pane, direnv)
|
|
122
123
|
symlinks: [] # untracked assets linked from main into new worktrees
|
|
123
124
|
setup_scripts: [] # shell snippets run in a fresh worktree
|
|
124
125
|
scripts: {} # name -> snippet for `wf run NAME`
|
|
125
126
|
```
|
|
126
127
|
|
|
127
|
-
Openers
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
### Openers
|
|
129
|
+
|
|
130
|
+
An opener is a shell command that runs with the worktree root as working
|
|
131
|
+
directory, either **in your terminal** (the default: the `wf` wrapper does
|
|
132
|
+
`cd` there and runs it) or **in the background** (`background: true`:
|
|
133
|
+
spawned detached, `wf` returns immediately — for GUI apps and commands that
|
|
134
|
+
hand off to a daemon or multiplexer). Optionally it runs **through a
|
|
135
|
+
wrapper**, a command that receives it as `$WF_COMMAND` and takes it
|
|
136
|
+
somewhere else: a new terminal window, a tmux window, a `direnv exec`. With
|
|
137
|
+
no config at all, `wf` runs `$VISUAL`/`$EDITOR` in your terminal.
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
opener: win # what `wf create` / `wf open` run by default
|
|
141
|
+
|
|
142
|
+
wrappers: # get the opener command as $WF_COMMAND
|
|
143
|
+
kitty:
|
|
144
|
+
command: kitty --title "$WF_TITLE" --directory "$WF_WORKTREE" $SHELL -c "$WF_COMMAND"
|
|
145
|
+
background: true
|
|
146
|
+
tmux: # the tmux server has its own environment: WF_ENV re-creates ours
|
|
147
|
+
command: tmux new-window -n "$WF_TITLE" -c "$WF_WORKTREE" "export $WF_ENV; $WF_COMMAND"
|
|
148
|
+
background: true
|
|
149
|
+
|
|
150
|
+
openers:
|
|
151
|
+
edit: $EDITOR "$WF_TARGET" # in your terminal
|
|
152
|
+
code: # GUI app: detached
|
|
153
|
+
command: code "$WF_WORKTREE"
|
|
154
|
+
background: true
|
|
155
|
+
kitty: # edit's command, in a new kitty window
|
|
156
|
+
from: edit
|
|
157
|
+
wrap: kitty
|
|
158
|
+
tmux: # edit's command, in a new tmux window
|
|
159
|
+
from: edit
|
|
160
|
+
wrap: tmux
|
|
161
|
+
git: lazygit
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
An entry is a shell command string, or a mapping with either `command` (a
|
|
165
|
+
shell command, never a name) or `from` (another opener's command — one
|
|
166
|
+
level: the target has a `command` of its own — inheriting its `background`
|
|
167
|
+
unless the entry sets one), plus optionally `background: true` or
|
|
168
|
+
`wrap: NAME`. `wrap` and `background` never sit on the same entry: the
|
|
169
|
+
wrapper decides where the whole thing runs, via its own `background` flag.
|
|
170
|
+
`-o VALUE` (and `opener:`) is an `openers` name, else a shell command;
|
|
171
|
+
`-w NAME` on `create`/`open` overrides the opener's wrapper (`-w ''` for
|
|
172
|
+
none). Wrappers are environment-specific, so they belong in the per-machine
|
|
173
|
+
user config — a host without your terminal emulator simply has none.
|
|
174
|
+
`from` and `wrap` names are checked when the config loads, so `wf config`
|
|
175
|
+
reports a misspelled one.
|
|
176
|
+
|
|
177
|
+
All of them are plain shell commands, run via `$SHELL -c` with one variable
|
|
178
|
+
family in the environment — the same family the launched process and every
|
|
179
|
+
script receive:
|
|
130
180
|
|
|
131
181
|
| Variable | Value |
|
|
132
182
|
|---|---|
|
|
@@ -137,18 +187,21 @@ process and every script receive:
|
|
|
137
187
|
| `WF_BRANCH` | its branch (empty if detached) |
|
|
138
188
|
| `WF_TARGET` | the `-p` argument, default `.` (launch-only) |
|
|
139
189
|
| `WF_TITLE` | window label, `project_name: feat-x` (launch-only) |
|
|
190
|
+
| `WF_ENV` | all of the above as shell-quoted `NAME=value` assignments (launch-only) |
|
|
191
|
+
| `WF_COMMAND` | in a wrapper: the opener command, unexpanded |
|
|
140
192
|
|
|
141
193
|
Standard shell rules apply — there is no workforest template syntax:
|
|
142
194
|
`"$WF_X"` is exactly one argument, bare `$WF_X` word-splits, and `$$`,
|
|
143
195
|
braces, pipes, and `&&` mean whatever your shell says they mean (a
|
|
144
|
-
misspelled `$WF_VAR` expands to empty, as in any shell).
|
|
145
|
-
the
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
196
|
+
misspelled `$WF_VAR` expands to empty, as in any shell). `$WF_COMMAND`
|
|
197
|
+
reaches the wrapper unexpanded, so run it through a shell of its own for its
|
|
198
|
+
`$WF_*` references to resolve: `$SHELL -c "$WF_COMMAND"`. When that shell
|
|
199
|
+
runs somewhere this environment is not inherited — a tmux server, an ssh
|
|
200
|
+
host — hand the family over as text: `"export $WF_ENV; $WF_COMMAND"` is
|
|
201
|
+
re-parsed on the far side, quoting intact. Background
|
|
202
|
+
processes shed activation state inherited from the invoking shell (Python
|
|
203
|
+
venv, conda, nvm, rvm) so a new window starts clean instead of carrying an
|
|
204
|
+
environment it cannot deactivate.
|
|
152
205
|
|
|
153
206
|
Fully commented reference configs:
|
|
154
207
|
[`config.yaml`](src/workforest/examples/config.yaml) (user/system) and
|
|
@@ -187,8 +240,8 @@ scripts:
|
|
|
187
240
|
## Commands
|
|
188
241
|
|
|
189
242
|
```
|
|
190
|
-
workforest create [BRANCH] [-o OPENER] [-p PATH] [--no-hooks] [--no-open]
|
|
191
|
-
workforest open [NAME] [-o OPENER] [-p PATH]
|
|
243
|
+
workforest create [BRANCH] [-o OPENER] [-w WRAPPER] [-p PATH] [--no-hooks] [--no-open]
|
|
244
|
+
workforest open [NAME] [-o OPENER] [-w WRAPPER] [-p PATH]
|
|
192
245
|
workforest list [--porcelain]
|
|
193
246
|
workforest delete NAME... [--force] [--delete-branch | --keep-branch]
|
|
194
247
|
workforest checkout NAME [--force]
|
|
@@ -24,8 +24,8 @@ opened, and cleaned up with one command.
|
|
|
24
24
|
- **Create** a worktree for any branch (local, remote, or brand new) and have
|
|
25
25
|
it set up automatically: symlinks for untracked assets (`node_modules`,
|
|
26
26
|
`.env`, …) and project-defined setup scripts.
|
|
27
|
-
- **Open** it in your editor — in
|
|
28
|
-
|
|
27
|
+
- **Open** it in your editor — in your terminal, in a new terminal window or
|
|
28
|
+
multiplexer pane, or in a GUI app, all from a small shell-command config.
|
|
29
29
|
- **Run** named project scripts with well-known `WF_*` environment variables.
|
|
30
30
|
- **Delete** worktrees safely, or **checkout**: collapse one back into the
|
|
31
31
|
main checkout.
|
|
@@ -86,27 +86,77 @@ Layered, YAML or JSON; later layers override earlier ones:
|
|
|
86
86
|
| project (shared) | `.workforest.yaml` in the repo root | repo policy, committed |
|
|
87
87
|
| project (local) | `.vscode/` or `.idea/` `.workforest.yaml` | personal overrides, untracked |
|
|
88
88
|
|
|
89
|
-
Scalars and lists replace; the `scripts
|
|
90
|
-
(`null` removes an entry). `workforest config` shows the
|
|
91
|
-
where each layer came from; `workforest init` scaffolds a
|
|
92
|
-
(`--local` for a personal one).
|
|
89
|
+
Scalars and lists replace; the `openers`/`wrappers`/`scripts` mappings
|
|
90
|
+
merge per key (`null` removes an entry). `workforest config` shows the
|
|
91
|
+
merged result and where each layer came from; `workforest init` scaffolds a
|
|
92
|
+
project file (`--local` for a personal one). Nothing in the environment
|
|
93
|
+
changes the result — files and flags only.
|
|
93
94
|
|
|
94
95
|
All keys, with defaults:
|
|
95
96
|
|
|
96
97
|
```yaml
|
|
97
98
|
worktrees_dir: "$WF_MAIN/../worktrees/$WF_NAME" # where the forest lives
|
|
98
|
-
opener: "" # default opener
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
opener: "" # default opener: an `openers` name or a shell command;
|
|
100
|
+
# "" → $VISUAL → $EDITOR
|
|
101
|
+
openers: {} # name -> what `-o NAME` runs, and where
|
|
102
|
+
wrappers: {} # name -> command that runs $WF_COMMAND elsewhere (window, pane, direnv)
|
|
102
103
|
symlinks: [] # untracked assets linked from main into new worktrees
|
|
103
104
|
setup_scripts: [] # shell snippets run in a fresh worktree
|
|
104
105
|
scripts: {} # name -> snippet for `wf run NAME`
|
|
105
106
|
```
|
|
106
107
|
|
|
107
|
-
Openers
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
### Openers
|
|
109
|
+
|
|
110
|
+
An opener is a shell command that runs with the worktree root as working
|
|
111
|
+
directory, either **in your terminal** (the default: the `wf` wrapper does
|
|
112
|
+
`cd` there and runs it) or **in the background** (`background: true`:
|
|
113
|
+
spawned detached, `wf` returns immediately — for GUI apps and commands that
|
|
114
|
+
hand off to a daemon or multiplexer). Optionally it runs **through a
|
|
115
|
+
wrapper**, a command that receives it as `$WF_COMMAND` and takes it
|
|
116
|
+
somewhere else: a new terminal window, a tmux window, a `direnv exec`. With
|
|
117
|
+
no config at all, `wf` runs `$VISUAL`/`$EDITOR` in your terminal.
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
opener: win # what `wf create` / `wf open` run by default
|
|
121
|
+
|
|
122
|
+
wrappers: # get the opener command as $WF_COMMAND
|
|
123
|
+
kitty:
|
|
124
|
+
command: kitty --title "$WF_TITLE" --directory "$WF_WORKTREE" $SHELL -c "$WF_COMMAND"
|
|
125
|
+
background: true
|
|
126
|
+
tmux: # the tmux server has its own environment: WF_ENV re-creates ours
|
|
127
|
+
command: tmux new-window -n "$WF_TITLE" -c "$WF_WORKTREE" "export $WF_ENV; $WF_COMMAND"
|
|
128
|
+
background: true
|
|
129
|
+
|
|
130
|
+
openers:
|
|
131
|
+
edit: $EDITOR "$WF_TARGET" # in your terminal
|
|
132
|
+
code: # GUI app: detached
|
|
133
|
+
command: code "$WF_WORKTREE"
|
|
134
|
+
background: true
|
|
135
|
+
kitty: # edit's command, in a new kitty window
|
|
136
|
+
from: edit
|
|
137
|
+
wrap: kitty
|
|
138
|
+
tmux: # edit's command, in a new tmux window
|
|
139
|
+
from: edit
|
|
140
|
+
wrap: tmux
|
|
141
|
+
git: lazygit
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
An entry is a shell command string, or a mapping with either `command` (a
|
|
145
|
+
shell command, never a name) or `from` (another opener's command — one
|
|
146
|
+
level: the target has a `command` of its own — inheriting its `background`
|
|
147
|
+
unless the entry sets one), plus optionally `background: true` or
|
|
148
|
+
`wrap: NAME`. `wrap` and `background` never sit on the same entry: the
|
|
149
|
+
wrapper decides where the whole thing runs, via its own `background` flag.
|
|
150
|
+
`-o VALUE` (and `opener:`) is an `openers` name, else a shell command;
|
|
151
|
+
`-w NAME` on `create`/`open` overrides the opener's wrapper (`-w ''` for
|
|
152
|
+
none). Wrappers are environment-specific, so they belong in the per-machine
|
|
153
|
+
user config — a host without your terminal emulator simply has none.
|
|
154
|
+
`from` and `wrap` names are checked when the config loads, so `wf config`
|
|
155
|
+
reports a misspelled one.
|
|
156
|
+
|
|
157
|
+
All of them are plain shell commands, run via `$SHELL -c` with one variable
|
|
158
|
+
family in the environment — the same family the launched process and every
|
|
159
|
+
script receive:
|
|
110
160
|
|
|
111
161
|
| Variable | Value |
|
|
112
162
|
|---|---|
|
|
@@ -117,18 +167,21 @@ process and every script receive:
|
|
|
117
167
|
| `WF_BRANCH` | its branch (empty if detached) |
|
|
118
168
|
| `WF_TARGET` | the `-p` argument, default `.` (launch-only) |
|
|
119
169
|
| `WF_TITLE` | window label, `project_name: feat-x` (launch-only) |
|
|
170
|
+
| `WF_ENV` | all of the above as shell-quoted `NAME=value` assignments (launch-only) |
|
|
171
|
+
| `WF_COMMAND` | in a wrapper: the opener command, unexpanded |
|
|
120
172
|
|
|
121
173
|
Standard shell rules apply — there is no workforest template syntax:
|
|
122
174
|
`"$WF_X"` is exactly one argument, bare `$WF_X` word-splits, and `$$`,
|
|
123
175
|
braces, pipes, and `&&` mean whatever your shell says they mean (a
|
|
124
|
-
misspelled `$WF_VAR` expands to empty, as in any shell).
|
|
125
|
-
the
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
176
|
+
misspelled `$WF_VAR` expands to empty, as in any shell). `$WF_COMMAND`
|
|
177
|
+
reaches the wrapper unexpanded, so run it through a shell of its own for its
|
|
178
|
+
`$WF_*` references to resolve: `$SHELL -c "$WF_COMMAND"`. When that shell
|
|
179
|
+
runs somewhere this environment is not inherited — a tmux server, an ssh
|
|
180
|
+
host — hand the family over as text: `"export $WF_ENV; $WF_COMMAND"` is
|
|
181
|
+
re-parsed on the far side, quoting intact. Background
|
|
182
|
+
processes shed activation state inherited from the invoking shell (Python
|
|
183
|
+
venv, conda, nvm, rvm) so a new window starts clean instead of carrying an
|
|
184
|
+
environment it cannot deactivate.
|
|
132
185
|
|
|
133
186
|
Fully commented reference configs:
|
|
134
187
|
[`config.yaml`](src/workforest/examples/config.yaml) (user/system) and
|
|
@@ -167,8 +220,8 @@ scripts:
|
|
|
167
220
|
## Commands
|
|
168
221
|
|
|
169
222
|
```
|
|
170
|
-
workforest create [BRANCH] [-o OPENER] [-p PATH] [--no-hooks] [--no-open]
|
|
171
|
-
workforest open [NAME] [-o OPENER] [-p PATH]
|
|
223
|
+
workforest create [BRANCH] [-o OPENER] [-w WRAPPER] [-p PATH] [--no-hooks] [--no-open]
|
|
224
|
+
workforest open [NAME] [-o OPENER] [-w WRAPPER] [-p PATH]
|
|
172
225
|
workforest list [--porcelain]
|
|
173
226
|
workforest delete NAME... [--force] [--delete-branch | --keep-branch]
|
|
174
227
|
workforest checkout NAME [--force]
|
|
@@ -68,6 +68,7 @@ def _handle_create(ns: argparse.Namespace) -> CommandResult:
|
|
|
68
68
|
ctx,
|
|
69
69
|
ns.branch,
|
|
70
70
|
opener=ns.opener,
|
|
71
|
+
wrap=ns.wrap,
|
|
71
72
|
path_arg=ns.path,
|
|
72
73
|
no_hooks=ns.no_hooks,
|
|
73
74
|
no_open=ns.no_open,
|
|
@@ -76,7 +77,7 @@ def _handle_create(ns: argparse.Namespace) -> CommandResult:
|
|
|
76
77
|
|
|
77
78
|
def _handle_open(ns: argparse.Namespace) -> CommandResult:
|
|
78
79
|
ctx = commands.build_context()
|
|
79
|
-
return commands.cmd_open(ctx, ns.name, opener=ns.opener, path_arg=ns.path)
|
|
80
|
+
return commands.cmd_open(ctx, ns.name, opener=ns.opener, wrap=ns.wrap, path_arg=ns.path)
|
|
80
81
|
|
|
81
82
|
|
|
82
83
|
def _handle_list(ns: argparse.Namespace) -> CommandResult:
|
|
@@ -149,6 +150,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
149
150
|
|
|
150
151
|
def opener_args(p: argparse.ArgumentParser) -> None:
|
|
151
152
|
p.add_argument("-o", "--opener", help="opener name or shell command")
|
|
153
|
+
p.add_argument(
|
|
154
|
+
"-w",
|
|
155
|
+
"--wrap",
|
|
156
|
+
metavar="WRAPPER",
|
|
157
|
+
help="run the opener through this `wrappers` entry ('' for none)",
|
|
158
|
+
)
|
|
152
159
|
p.add_argument(
|
|
153
160
|
"-p", "--path", help="path inside the worktree, passed to the opener as $WF_TARGET"
|
|
154
161
|
)
|
|
@@ -118,6 +118,7 @@ def cmd_create(
|
|
|
118
118
|
branch: str | None,
|
|
119
119
|
*,
|
|
120
120
|
opener: str | None = None,
|
|
121
|
+
wrap: str | None = None,
|
|
121
122
|
path_arg: str | None = None,
|
|
122
123
|
no_hooks: bool = False,
|
|
123
124
|
no_open: bool = False,
|
|
@@ -164,6 +165,7 @@ def cmd_create(
|
|
|
164
165
|
worktrees_dir=ctx.worktrees_dir,
|
|
165
166
|
branch=branch,
|
|
166
167
|
opener_arg=opener,
|
|
168
|
+
wrap_arg=wrap,
|
|
167
169
|
path_arg=path_arg,
|
|
168
170
|
)
|
|
169
171
|
|
|
@@ -173,6 +175,7 @@ def cmd_open(
|
|
|
173
175
|
name: str | None,
|
|
174
176
|
*,
|
|
175
177
|
opener: str | None = None,
|
|
178
|
+
wrap: str | None = None,
|
|
176
179
|
path_arg: str | None = None,
|
|
177
180
|
) -> CommandResult:
|
|
178
181
|
if name:
|
|
@@ -190,6 +193,7 @@ def cmd_open(
|
|
|
190
193
|
worktrees_dir=ctx.worktrees_dir,
|
|
191
194
|
branch=worktree.branch,
|
|
192
195
|
opener_arg=opener,
|
|
196
|
+
wrap_arg=wrap,
|
|
193
197
|
path_arg=path_arg,
|
|
194
198
|
)
|
|
195
199
|
|
|
@@ -7,7 +7,7 @@ names; the `commands` topic emits `NAME<TAB>KIND<TAB>DESCRIPTION` and the
|
|
|
7
7
|
(zsh) do, while others take field 1.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from workforest import commands, gitutil
|
|
10
|
+
from workforest import commands, gitutil, launch
|
|
11
11
|
from workforest.config import Config, load_config
|
|
12
12
|
from workforest.errors import WorkforestError
|
|
13
13
|
|
|
@@ -50,13 +50,13 @@ def _commands() -> list[str]:
|
|
|
50
50
|
from workforest.cli import SUBCOMMAND_HELP, _known_subcommands
|
|
51
51
|
|
|
52
52
|
known = _known_subcommands()
|
|
53
|
-
|
|
53
|
+
config = _config()
|
|
54
54
|
# An opener sharing a subcommand's name is shadowed by it (cli._preprocess
|
|
55
55
|
# dispatches known subcommands first), so don't offer it.
|
|
56
56
|
return [f"{name}\tcommand\t{SUBCOMMAND_HELP[name]}" for name in sorted(known)] + [
|
|
57
57
|
# collapse whitespace: a tab/newline in an opener command would break the line protocol
|
|
58
|
-
f"{name}\topener\t{' '.join(
|
|
59
|
-
for name in sorted(openers)
|
|
58
|
+
f"{name}\topener\t{' '.join(launch.describe_opener(config, name).split())}"
|
|
59
|
+
for name in sorted(config.openers)
|
|
60
60
|
if name not in known
|
|
61
61
|
]
|
|
62
62
|
|