gitdirector 1.6.0__tar.gz → 1.7.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.
- {gitdirector-1.6.0 → gitdirector-1.7.2}/PKG-INFO +27 -6
- {gitdirector-1.6.0 → gitdirector-1.7.2}/README.md +26 -5
- {gitdirector-1.6.0 → gitdirector-1.7.2}/pyproject.toml +1 -1
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/cli.py +2 -0
- gitdirector-1.7.2/src/gitdirector/commands/completion.py +74 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/gd_tmux.py +9 -1
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/help.py +4 -2
- gitdirector-1.7.2/src/gitdirector/commands/reset.py +82 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/app.py +128 -26
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/app_sessions.py +1 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/app_ui.py +2 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/panel_view.py +15 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/panels.py +20 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/commit.py +38 -13
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/diff.py +1 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/panels.py +33 -8
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/repos.py +7 -7
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/terminal_caps.py +4 -0
- gitdirector-1.7.2/src/gitdirector/commands/tui/terminal_widget.py +901 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/config.py +85 -48
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/integrations/tmux/core.py +194 -14
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/integrations/tmux/monitor.py +77 -13
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/integrations/tmux/panels.py +286 -102
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/repo.py +4 -0
- gitdirector-1.6.0/src/gitdirector/commands/completion.py +0 -41
- gitdirector-1.6.0/src/gitdirector/commands/tui/terminal_widget.py +0 -482
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/__init__.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/__init__.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/autoclean.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/capture.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/cd.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/gd_send.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/info.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/link.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/listt.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/pull.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/status.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/__init__.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/app_groups.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/app_panels.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/app_repos.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/constants.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/diff_renderer.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/__init__.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/_shared.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/diff_files.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/groups.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/session_actions.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/screens/sessions.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/tui/table_text.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/commands/unlink.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/github_credential_helper.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/info.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/integrations/__init__.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/integrations/tmux/__init__.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/manager.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/storage.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/ui_theme.py +0 -0
- {gitdirector-1.6.0 → gitdirector-1.7.2}/src/gitdirector/version_check.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: gitdirector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.2
|
|
4
4
|
Summary: A terminal based control plane for developers working across multiple repositories. Launch multiple AI coding agents, multiple tmux sessions and track changes across all your repos in one place.
|
|
5
5
|
Keywords: git,repository,manager,cli,synchronization,batch
|
|
6
6
|
Author: Anito Anto
|
|
@@ -74,6 +74,7 @@ If you find GitDirector useful, please star this repository on GitHub, we need m
|
|
|
74
74
|
| `gitdirector pull [--yes]` | Pull latest changes for all tracked repositories |
|
|
75
75
|
| `gitdirector cd NAME` | Open or switch to a tmux session for a repository |
|
|
76
76
|
| `gitdirector autoclean` | Remove broken repository links from tracking |
|
|
77
|
+
| `gitdirector reset [--yes]` | Kill all sessions and panels, wipe ~/.gitdirector, and recreate the config |
|
|
77
78
|
| `gitdirector info PATH\|NAME [--full]` | Show file statistics for a repository |
|
|
78
79
|
| `gitdirector gd-tmux PATH\|NAME "command" [--description TEXT]` | Create a gd tmux session and run a command in it |
|
|
79
80
|
| `gitdirector gd-capture SESSION [--lines N] [--full]` | Print the scrollback of a live gd tmux session |
|
|
@@ -104,17 +105,17 @@ Features:
|
|
|
104
105
|
- `/` to filter the active tab
|
|
105
106
|
- `s` to sort the active table
|
|
106
107
|
- `i` to show repository info (file count, lines, tokens, max depth, top file types)
|
|
107
|
-
- `g` on the Repositories tab to open Git operations: status, timeline, branches, remotes, pull, and
|
|
108
|
+
- `g` on the Repositories tab to open Git operations: status, timeline, branches, remotes, pull, push, and a **Diff Viewer** section with **Review Diff** for uncommitted changes
|
|
108
109
|
- `r` to refresh all statuses
|
|
109
110
|
- Press `enter` on any repository to open an action menu:
|
|
110
111
|
- **New tmux session** — create and attach a session for the repository
|
|
111
|
-
- **Review Diff** — open a two-pane viewer for uncommitted changes (left: file list with GitHub-style status pills and +/- counts; right: per-file unified diff with GitHub-dark syntax highlighting, line numbers, and green/red backgrounds for added/removed lines). Navigate files with `j`/`k`, `n`/`p`, `g`/`G`, or `]`/`[`. Switch the focused panel with `tab`. `r` refreshes, `esc` closes.
|
|
112
112
|
- **Attach existing session** — switch to any already-running tmux session
|
|
113
113
|
- **Launch AI agent** — open OpenCode, Claude Code, GitHub Copilot, Codex, or Pi in a new tmux session
|
|
114
114
|
- **Remove session** — kill a running tmux session
|
|
115
|
+
- **Review Diff** (from the `g` Git menu) — open a two-pane viewer for uncommitted changes (left: file list with GitHub-style status pills and +/- counts; right: per-file unified diff with GitHub-dark syntax highlighting, line numbers, and green/red backgrounds for added/removed lines). Navigate files with `j`/`k`, `n`/`p`, `g`/`G`, or `]`/`[`. Switch the focused panel with `tab`. `r` refreshes, `esc` closes.
|
|
115
116
|
- **Repository groups** appear directly in the Repositories tab when linked repositories share the same parent directory. Group rows can be expanded/collapsed with `space`; press `enter` on a group to start, attach, remove, or launch AI-agent sessions rooted at that parent directory.
|
|
116
117
|
- **Sessions tab** (press `2`) lists every active `gd/*` tmux session with its status, purpose, repository, **description**, and full session name. The description column is free-form text stored on the session (default `"-"`), with a width that scales to your terminal and wraps long text. Highlight a row and press `d` to edit its description.
|
|
117
|
-
- **Panels tab** (press `3`) manages reusable tmux panel layouts. Press `n` to create a panel, or `enter` on a panel to open, reconfigure, rename, or delete it.
|
|
118
|
+
- **Panels tab** (press `3`) manages reusable tmux panel layouts. Press `n` to create a panel, or `enter` on a panel to open, reconfigure, rename, or delete it. Inside a panel you can **drag with the mouse to select text and copy it to the OS clipboard** on release; pressing `y` copies the entire visible pane as a fallback. GitDirector prefers native clipboard tools such as `pbcopy`, `wl-copy`, `xclip`, `xsel`, or `clip.exe`, and falls back to OSC 52 when needed.
|
|
118
119
|
|
|
119
120
|
### unlink
|
|
120
121
|
|
|
@@ -223,6 +224,15 @@ gitdirector autoclean
|
|
|
223
224
|
|
|
224
225
|
Scans the tracked repositories in `~/.gitdirector/config.yaml` for paths that no longer exist on disk and removes them from the config. Lists any broken links for review and asks for confirmation before deleting them.
|
|
225
226
|
|
|
227
|
+
### reset
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
gitdirector reset # asks for confirmation
|
|
231
|
+
gitdirector reset --yes # skip the confirmation prompt
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Destructive cleanup. Kills every `gd/*` tmux session (regular sessions and panels), wipes the entire `~/.gitdirector/` directory, and recreates an empty `config.yaml` with defaults. Useful when the local state is corrupted or you want to start fresh — all linked repositories, panel layouts, version-check cache, and tmux theme files are removed in one step.
|
|
235
|
+
|
|
226
236
|
### gd-capture
|
|
227
237
|
|
|
228
238
|
```bash
|
|
@@ -263,7 +273,7 @@ If you are an AI coding agent (Claude Code, OpenCode, GitHub Copilot, Codex, Pi,
|
|
|
263
273
|
|
|
264
274
|
## Configuration
|
|
265
275
|
|
|
266
|
-
|
|
276
|
+
Main config is stored at `~/.gitdirector/config.yaml`:
|
|
267
277
|
|
|
268
278
|
```yaml
|
|
269
279
|
repositories:
|
|
@@ -271,6 +281,11 @@ repositories:
|
|
|
271
281
|
- /path/to/repo2
|
|
272
282
|
max_workers: 10 # optional, valid range 1-32, default 10
|
|
273
283
|
theme: rose-pine # optional, default rose-pine
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Secrets (GitHub credentials) are stored in a separate file at `~/.gitdirector/secrets.yaml`:
|
|
287
|
+
|
|
288
|
+
```yaml
|
|
274
289
|
github_username: your-github-username # optional
|
|
275
290
|
github_PAT: github_pat_or_classic_pat # optional
|
|
276
291
|
```
|
|
@@ -281,7 +296,7 @@ GitDirector first runs git commands using your normal local git credentials. If
|
|
|
281
296
|
|
|
282
297
|
- This only applies to HTTPS GitHub remotes.
|
|
283
298
|
- SSH remotes still use your normal SSH key/agent setup.
|
|
284
|
-
- The PAT is stored in plaintext in `~/.gitdirector/
|
|
299
|
+
- The PAT is stored in plaintext in `~/.gitdirector/secrets.yaml`; use an appropriately scoped token and protect that file.
|
|
285
300
|
- The PAT is not passed on the command line or shown in TUI command output.
|
|
286
301
|
|
|
287
302
|
### Available Themes
|
|
@@ -304,6 +319,12 @@ eval "$(gitdirector completion zsh)"
|
|
|
304
319
|
gitdirector completion fish | source
|
|
305
320
|
```
|
|
306
321
|
|
|
322
|
+
The `zsh` script auto-loads `compinit` if `compdef` is not yet available, so the `eval` form works in fresh shells. For the fastest setup (no subprocess on every `TAB`), write the script to a file in your `$fpath` (commonly `~/.zsh/completions/_gitdirector`) and let `compinit` autoload it:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
gitdirector completion zsh > "${fpath[1]}/_gitdirector"
|
|
326
|
+
```
|
|
327
|
+
|
|
307
328
|
## License
|
|
308
329
|
|
|
309
330
|
MIT
|
|
@@ -32,6 +32,7 @@ If you find GitDirector useful, please star this repository on GitHub, we need m
|
|
|
32
32
|
| `gitdirector pull [--yes]` | Pull latest changes for all tracked repositories |
|
|
33
33
|
| `gitdirector cd NAME` | Open or switch to a tmux session for a repository |
|
|
34
34
|
| `gitdirector autoclean` | Remove broken repository links from tracking |
|
|
35
|
+
| `gitdirector reset [--yes]` | Kill all sessions and panels, wipe ~/.gitdirector, and recreate the config |
|
|
35
36
|
| `gitdirector info PATH\|NAME [--full]` | Show file statistics for a repository |
|
|
36
37
|
| `gitdirector gd-tmux PATH\|NAME "command" [--description TEXT]` | Create a gd tmux session and run a command in it |
|
|
37
38
|
| `gitdirector gd-capture SESSION [--lines N] [--full]` | Print the scrollback of a live gd tmux session |
|
|
@@ -62,17 +63,17 @@ Features:
|
|
|
62
63
|
- `/` to filter the active tab
|
|
63
64
|
- `s` to sort the active table
|
|
64
65
|
- `i` to show repository info (file count, lines, tokens, max depth, top file types)
|
|
65
|
-
- `g` on the Repositories tab to open Git operations: status, timeline, branches, remotes, pull, and
|
|
66
|
+
- `g` on the Repositories tab to open Git operations: status, timeline, branches, remotes, pull, push, and a **Diff Viewer** section with **Review Diff** for uncommitted changes
|
|
66
67
|
- `r` to refresh all statuses
|
|
67
68
|
- Press `enter` on any repository to open an action menu:
|
|
68
69
|
- **New tmux session** — create and attach a session for the repository
|
|
69
|
-
- **Review Diff** — open a two-pane viewer for uncommitted changes (left: file list with GitHub-style status pills and +/- counts; right: per-file unified diff with GitHub-dark syntax highlighting, line numbers, and green/red backgrounds for added/removed lines). Navigate files with `j`/`k`, `n`/`p`, `g`/`G`, or `]`/`[`. Switch the focused panel with `tab`. `r` refreshes, `esc` closes.
|
|
70
70
|
- **Attach existing session** — switch to any already-running tmux session
|
|
71
71
|
- **Launch AI agent** — open OpenCode, Claude Code, GitHub Copilot, Codex, or Pi in a new tmux session
|
|
72
72
|
- **Remove session** — kill a running tmux session
|
|
73
|
+
- **Review Diff** (from the `g` Git menu) — open a two-pane viewer for uncommitted changes (left: file list with GitHub-style status pills and +/- counts; right: per-file unified diff with GitHub-dark syntax highlighting, line numbers, and green/red backgrounds for added/removed lines). Navigate files with `j`/`k`, `n`/`p`, `g`/`G`, or `]`/`[`. Switch the focused panel with `tab`. `r` refreshes, `esc` closes.
|
|
73
74
|
- **Repository groups** appear directly in the Repositories tab when linked repositories share the same parent directory. Group rows can be expanded/collapsed with `space`; press `enter` on a group to start, attach, remove, or launch AI-agent sessions rooted at that parent directory.
|
|
74
75
|
- **Sessions tab** (press `2`) lists every active `gd/*` tmux session with its status, purpose, repository, **description**, and full session name. The description column is free-form text stored on the session (default `"-"`), with a width that scales to your terminal and wraps long text. Highlight a row and press `d` to edit its description.
|
|
75
|
-
- **Panels tab** (press `3`) manages reusable tmux panel layouts. Press `n` to create a panel, or `enter` on a panel to open, reconfigure, rename, or delete it.
|
|
76
|
+
- **Panels tab** (press `3`) manages reusable tmux panel layouts. Press `n` to create a panel, or `enter` on a panel to open, reconfigure, rename, or delete it. Inside a panel you can **drag with the mouse to select text and copy it to the OS clipboard** on release; pressing `y` copies the entire visible pane as a fallback. GitDirector prefers native clipboard tools such as `pbcopy`, `wl-copy`, `xclip`, `xsel`, or `clip.exe`, and falls back to OSC 52 when needed.
|
|
76
77
|
|
|
77
78
|
### unlink
|
|
78
79
|
|
|
@@ -181,6 +182,15 @@ gitdirector autoclean
|
|
|
181
182
|
|
|
182
183
|
Scans the tracked repositories in `~/.gitdirector/config.yaml` for paths that no longer exist on disk and removes them from the config. Lists any broken links for review and asks for confirmation before deleting them.
|
|
183
184
|
|
|
185
|
+
### reset
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
gitdirector reset # asks for confirmation
|
|
189
|
+
gitdirector reset --yes # skip the confirmation prompt
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Destructive cleanup. Kills every `gd/*` tmux session (regular sessions and panels), wipes the entire `~/.gitdirector/` directory, and recreates an empty `config.yaml` with defaults. Useful when the local state is corrupted or you want to start fresh — all linked repositories, panel layouts, version-check cache, and tmux theme files are removed in one step.
|
|
193
|
+
|
|
184
194
|
### gd-capture
|
|
185
195
|
|
|
186
196
|
```bash
|
|
@@ -221,7 +231,7 @@ If you are an AI coding agent (Claude Code, OpenCode, GitHub Copilot, Codex, Pi,
|
|
|
221
231
|
|
|
222
232
|
## Configuration
|
|
223
233
|
|
|
224
|
-
|
|
234
|
+
Main config is stored at `~/.gitdirector/config.yaml`:
|
|
225
235
|
|
|
226
236
|
```yaml
|
|
227
237
|
repositories:
|
|
@@ -229,6 +239,11 @@ repositories:
|
|
|
229
239
|
- /path/to/repo2
|
|
230
240
|
max_workers: 10 # optional, valid range 1-32, default 10
|
|
231
241
|
theme: rose-pine # optional, default rose-pine
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Secrets (GitHub credentials) are stored in a separate file at `~/.gitdirector/secrets.yaml`:
|
|
245
|
+
|
|
246
|
+
```yaml
|
|
232
247
|
github_username: your-github-username # optional
|
|
233
248
|
github_PAT: github_pat_or_classic_pat # optional
|
|
234
249
|
```
|
|
@@ -239,7 +254,7 @@ GitDirector first runs git commands using your normal local git credentials. If
|
|
|
239
254
|
|
|
240
255
|
- This only applies to HTTPS GitHub remotes.
|
|
241
256
|
- SSH remotes still use your normal SSH key/agent setup.
|
|
242
|
-
- The PAT is stored in plaintext in `~/.gitdirector/
|
|
257
|
+
- The PAT is stored in plaintext in `~/.gitdirector/secrets.yaml`; use an appropriately scoped token and protect that file.
|
|
243
258
|
- The PAT is not passed on the command line or shown in TUI command output.
|
|
244
259
|
|
|
245
260
|
### Available Themes
|
|
@@ -262,6 +277,12 @@ eval "$(gitdirector completion zsh)"
|
|
|
262
277
|
gitdirector completion fish | source
|
|
263
278
|
```
|
|
264
279
|
|
|
280
|
+
The `zsh` script auto-loads `compinit` if `compdef` is not yet available, so the `eval` form works in fresh shells. For the fastest setup (no subprocess on every `TAB`), write the script to a file in your `$fpath` (commonly `~/.zsh/completions/_gitdirector`) and let `compinit` autoload it:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
gitdirector completion zsh > "${fpath[1]}/_gitdirector"
|
|
284
|
+
```
|
|
285
|
+
|
|
265
286
|
## License
|
|
266
287
|
|
|
267
288
|
MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitdirector"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.7.2"
|
|
8
8
|
description = "A terminal based control plane for developers working across multiple repositories. Launch multiple AI coding agents, multiple tmux sessions and track changes across all your repos in one place."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -20,6 +20,7 @@ from .commands import (
|
|
|
20
20
|
listt,
|
|
21
21
|
print_update_notice,
|
|
22
22
|
pull,
|
|
23
|
+
reset,
|
|
23
24
|
status,
|
|
24
25
|
tui,
|
|
25
26
|
unlink,
|
|
@@ -59,6 +60,7 @@ cd.register(cli)
|
|
|
59
60
|
help.register(cli)
|
|
60
61
|
tui.register(cli)
|
|
61
62
|
autoclean.register(cli)
|
|
63
|
+
reset.register(cli)
|
|
62
64
|
info.register(cli)
|
|
63
65
|
gd_tmux.register(cli)
|
|
64
66
|
capture.register(cli)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from click.shell_completion import CompletionItem, get_completion_class
|
|
3
|
+
|
|
4
|
+
from ..config import Config
|
|
5
|
+
|
|
6
|
+
SUPPORTED_SHELLS = ("bash", "zsh", "fish")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _completion_var(prog_name: str) -> str:
|
|
10
|
+
return f"_{prog_name.replace('-', '_').replace('.', '_').upper()}_COMPLETE"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_ZSH_COMPINIT_BOOTSTRAP = (
|
|
14
|
+
"\n"
|
|
15
|
+
"# Ensure ``compdef`` is available when this script is ``eval``'d in a\n"
|
|
16
|
+
"# shell that has not yet loaded ``compinit``. Without this guard the\n"
|
|
17
|
+
"# ``compdef`` call below fails with ``command not found: compdef``.\n"
|
|
18
|
+
"if ! typeset -f compdef >/dev/null 2>&1; then\n"
|
|
19
|
+
" autoload -U +X compinit\n"
|
|
20
|
+
" compinit\n"
|
|
21
|
+
"fi\n"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _patch_zsh_source(source: str) -> str:
|
|
26
|
+
"""Inject a compinit bootstrap so ``eval`` works without prior setup.
|
|
27
|
+
|
|
28
|
+
Click's zsh output ends with ``compdef ...`` which requires the
|
|
29
|
+
``compdef`` builtin provided by ``compinit``. When the script is
|
|
30
|
+
eval'd in a fresh zsh that hasn't loaded ``compinit`` yet, that
|
|
31
|
+
final call fails. Inserting a bootstrap right after the
|
|
32
|
+
``#compdef`` directive ensures ``compdef`` is available without
|
|
33
|
+
changing behavior when the script is dropped into ``$fpath`` and
|
|
34
|
+
autoloaded by ``compinit`` (in which case ``compdef`` already
|
|
35
|
+
exists and the bootstrap is a no-op).
|
|
36
|
+
"""
|
|
37
|
+
marker = "\n_gitdirector_completion() {"
|
|
38
|
+
if marker not in source:
|
|
39
|
+
return source
|
|
40
|
+
return source.replace(marker, _ZSH_COMPINIT_BOOTSTRAP + "\n_gitdirector_completion() {", 1)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def completion_source(cli: click.Group, shell: str, *, prog_name: str = "gitdirector") -> str:
|
|
44
|
+
complete_cls = get_completion_class(shell)
|
|
45
|
+
if complete_cls is None:
|
|
46
|
+
raise click.ClickException(f"Unsupported shell: {shell}")
|
|
47
|
+
source = complete_cls(cli, {}, prog_name, _completion_var(prog_name)).source()
|
|
48
|
+
if shell == "zsh":
|
|
49
|
+
source = _patch_zsh_source(source)
|
|
50
|
+
return source
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def complete_repository_names(
|
|
54
|
+
_ctx: click.Context, _param: click.Parameter, incomplete: str
|
|
55
|
+
) -> list[CompletionItem]:
|
|
56
|
+
try:
|
|
57
|
+
repositories = Config().repositories
|
|
58
|
+
except (OSError, RuntimeError, ValueError):
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
prefix = incomplete.lower()
|
|
62
|
+
return [
|
|
63
|
+
CompletionItem(repo.name, help=str(repo))
|
|
64
|
+
for repo in sorted(repositories, key=lambda path: (path.name.lower(), str(path)))
|
|
65
|
+
if repo.name.lower().startswith(prefix)
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def register(cli: click.Group):
|
|
70
|
+
@cli.command()
|
|
71
|
+
@click.argument("shell", type=click.Choice(SUPPORTED_SHELLS))
|
|
72
|
+
def completion(shell: str):
|
|
73
|
+
"""Print shell completion setup for bash, zsh, or fish."""
|
|
74
|
+
click.echo(completion_source(cli, shell), nl=False)
|
|
@@ -73,6 +73,7 @@ def register(cli: click.Group):
|
|
|
73
73
|
from ..integrations.tmux import (
|
|
74
74
|
TmuxError,
|
|
75
75
|
create_tmux_session,
|
|
76
|
+
kill_tmux_session,
|
|
76
77
|
launch_command_in_tmux_session,
|
|
77
78
|
)
|
|
78
79
|
except ImportError:
|
|
@@ -82,6 +83,7 @@ def register(cli: click.Group):
|
|
|
82
83
|
)
|
|
83
84
|
raise SystemExit(1)
|
|
84
85
|
|
|
86
|
+
session_name: str | None = None
|
|
85
87
|
try:
|
|
86
88
|
session_name = create_tmux_session(
|
|
87
89
|
repo_path.name, repo_path, purpose="shell", description=description
|
|
@@ -89,5 +91,11 @@ def register(cli: click.Group):
|
|
|
89
91
|
click.echo(session_name)
|
|
90
92
|
launch_command_in_tmux_session(session_name, command)
|
|
91
93
|
except TmuxError as exc:
|
|
94
|
+
if session_name is not None:
|
|
95
|
+
kill_tmux_session(session_name)
|
|
92
96
|
console.print(f"\n [red]tmux command failed:[/red] {exc}\n")
|
|
93
|
-
raise SystemExit(1)
|
|
97
|
+
raise SystemExit(1) from exc
|
|
98
|
+
except BaseException:
|
|
99
|
+
if session_name is not None:
|
|
100
|
+
kill_tmux_session(session_name)
|
|
101
|
+
raise
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from rich.table import Table
|
|
3
|
+
from rich.text import Text
|
|
3
4
|
|
|
4
5
|
from . import console, get_version, print_update_notice
|
|
5
6
|
|
|
@@ -33,6 +34,7 @@ def show_help():
|
|
|
33
34
|
("cd NAME", "Open or switch to a tmux session for a repository"),
|
|
34
35
|
("console", "Interactive TUI for browsing and opening repositories"),
|
|
35
36
|
("autoclean", "Remove broken repository links from tracking"),
|
|
37
|
+
("reset [--yes]", "Kill all sessions and panels, wipe ~/.gitdirector, recreate config"),
|
|
36
38
|
("info PATH|NAME [--full]", "Show file statistics for a repository"),
|
|
37
39
|
(
|
|
38
40
|
'gd-tmux PATH|NAME "cmd" [--description "..."]',
|
|
@@ -42,11 +44,11 @@ def show_help():
|
|
|
42
44
|
"gd-capture SESSION [--lines N] [--full]",
|
|
43
45
|
"Print the current scrollback of a live gd tmux session",
|
|
44
46
|
),
|
|
45
|
-
("gd-send SESSION [TEXT] [--enter
|
|
47
|
+
("gd-send SESSION [TEXT] [--enter | --key C-c]", "Send text or C-c to a gd session"),
|
|
46
48
|
("completion SHELL", "Print shell completion setup for bash, zsh, or fish"),
|
|
47
49
|
("help", "Show this help message"),
|
|
48
50
|
]:
|
|
49
|
-
cmd_table.add_row(cmd, desc)
|
|
51
|
+
cmd_table.add_row(Text(cmd), desc)
|
|
50
52
|
|
|
51
53
|
console.print(cmd_table)
|
|
52
54
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
from ..config import Config
|
|
7
|
+
from . import console
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def register(cli: click.Group):
|
|
11
|
+
@cli.command()
|
|
12
|
+
@click.option(
|
|
13
|
+
"--yes",
|
|
14
|
+
"-y",
|
|
15
|
+
is_flag=True,
|
|
16
|
+
help="Skip the confirmation prompt and reset immediately.",
|
|
17
|
+
)
|
|
18
|
+
def reset(yes: bool):
|
|
19
|
+
"""Kill all sessions and panels, wipe ~/.gitdirector, and recreate config."""
|
|
20
|
+
_reset(confirm=not yes)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _reset(*, confirm: bool = True) -> None:
|
|
24
|
+
config_dir = Path.home() / ".gitdirector"
|
|
25
|
+
|
|
26
|
+
if confirm and not click.confirm(
|
|
27
|
+
f" This will kill every gd tmux session and permanently delete {config_dir}. Continue?",
|
|
28
|
+
default=False,
|
|
29
|
+
):
|
|
30
|
+
console.print()
|
|
31
|
+
console.print(" [dim]Cancelled.[/dim]")
|
|
32
|
+
console.print()
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
console.print()
|
|
36
|
+
|
|
37
|
+
killed = _kill_all_sessions()
|
|
38
|
+
if killed:
|
|
39
|
+
console.print(f" [green]Killed {len(killed)} session(s):[/green]")
|
|
40
|
+
for name in killed:
|
|
41
|
+
console.print(f" [dim]-[/dim] {name}")
|
|
42
|
+
else:
|
|
43
|
+
console.print(" [dim]No active gd tmux sessions to kill.[/dim]")
|
|
44
|
+
|
|
45
|
+
_wipe_config_dir(config_dir)
|
|
46
|
+
|
|
47
|
+
config = _recreate_config()
|
|
48
|
+
|
|
49
|
+
console.print()
|
|
50
|
+
console.print(f" [green]Wiped and recreated:[/green] {config.config_file}")
|
|
51
|
+
console.print()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _kill_all_sessions() -> list[str]:
|
|
55
|
+
try:
|
|
56
|
+
from ..integrations.tmux import kill_all_gd_sessions
|
|
57
|
+
except ImportError:
|
|
58
|
+
console.print(" [yellow]tmux integration unavailable; skipping session kill.[/yellow]")
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
return kill_all_gd_sessions()
|
|
63
|
+
except Exception as exc:
|
|
64
|
+
console.print(f" [yellow]Failed to kill some sessions: {exc}[/yellow]")
|
|
65
|
+
return []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _wipe_config_dir(config_dir: Path) -> None:
|
|
69
|
+
if not config_dir.exists():
|
|
70
|
+
console.print(f" [dim]No existing {config_dir} to remove.[/dim]")
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
shutil.rmtree(config_dir)
|
|
75
|
+
except OSError as exc:
|
|
76
|
+
raise RuntimeError(f"Failed to remove {config_dir}: {exc}") from exc
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _recreate_config() -> Config:
|
|
80
|
+
config = Config()
|
|
81
|
+
config.clear()
|
|
82
|
+
return config
|