gitdirector 1.7.0__tar.gz → 1.7.3__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 (61) hide show
  1. {gitdirector-1.7.0 → gitdirector-1.7.3}/PKG-INFO +31 -7
  2. {gitdirector-1.7.0 → gitdirector-1.7.3}/README.md +29 -6
  3. {gitdirector-1.7.0 → gitdirector-1.7.3}/pyproject.toml +2 -1
  4. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/cli.py +2 -0
  5. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/capture.py +1 -2
  6. gitdirector-1.7.3/src/gitdirector/commands/completion.py +74 -0
  7. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/gd_send.py +2 -1
  8. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/gd_tmux.py +6 -19
  9. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/help.py +8 -3
  10. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/info.py +11 -20
  11. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/link.py +15 -0
  12. gitdirector-1.7.3/src/gitdirector/commands/reset.py +82 -0
  13. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/__init__.py +1 -2
  14. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/app.py +60 -27
  15. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/app_groups.py +1 -1
  16. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/app_panels.py +16 -15
  17. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/app_repos.py +16 -8
  18. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/app_sessions.py +5 -7
  19. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/app_ui.py +12 -0
  20. gitdirector-1.7.3/src/gitdirector/commands/tui/panel_view.py +211 -0
  21. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/panels.py +112 -16
  22. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/commit.py +38 -13
  23. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/diff.py +1 -0
  24. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/panels.py +17 -7
  25. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/repos.py +7 -7
  26. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/terminal_caps.py +4 -0
  27. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/terminal_widget.py +343 -22
  28. gitdirector-1.7.3/src/gitdirector/commands/unlink.py +48 -0
  29. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/config.py +122 -57
  30. gitdirector-1.7.3/src/gitdirector/integrations/tmux/__init__.py +106 -0
  31. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/integrations/tmux/core.py +142 -12
  32. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/integrations/tmux/monitor.py +8 -2
  33. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/integrations/tmux/panels.py +85 -21
  34. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/manager.py +83 -1
  35. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/repo.py +11 -0
  36. gitdirector-1.7.0/src/gitdirector/commands/completion.py +0 -41
  37. gitdirector-1.7.0/src/gitdirector/commands/tui/panel_view.py +0 -478
  38. gitdirector-1.7.0/src/gitdirector/commands/unlink.py +0 -48
  39. gitdirector-1.7.0/src/gitdirector/integrations/tmux/__init__.py +0 -5
  40. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/__init__.py +0 -0
  41. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/__init__.py +0 -0
  42. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/autoclean.py +0 -0
  43. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/cd.py +0 -0
  44. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/listt.py +0 -0
  45. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/pull.py +0 -0
  46. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/status.py +0 -0
  47. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/constants.py +0 -0
  48. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/diff_renderer.py +0 -0
  49. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/__init__.py +0 -0
  50. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/_shared.py +0 -0
  51. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/diff_files.py +0 -0
  52. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/groups.py +0 -0
  53. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/session_actions.py +0 -0
  54. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/screens/sessions.py +0 -0
  55. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/commands/tui/table_text.py +0 -0
  56. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/github_credential_helper.py +0 -0
  57. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/info.py +0 -0
  58. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/integrations/__init__.py +0 -0
  59. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/storage.py +0 -0
  60. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/ui_theme.py +0 -0
  61. {gitdirector-1.7.0 → gitdirector-1.7.3}/src/gitdirector/version_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: gitdirector
3
- Version: 1.7.0
3
+ Version: 1.7.3
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
@@ -24,6 +24,7 @@ Requires-Dist: rich>=12.0
24
24
  Requires-Dist: textual>=8.2.1
25
25
  Requires-Dist: tiktoken>=0.5.0
26
26
  Requires-Dist: pyte>=0.8.2
27
+ Requires-Dist: pathspec>=1.0.0
27
28
  Requires-Dist: pytest>=7.0 ; extra == 'dev'
28
29
  Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
29
30
  Requires-Dist: pytest-cov>=4.0 ; extra == 'dev'
@@ -67,13 +68,14 @@ If you find GitDirector useful, please star this repository on GitHub, we need m
67
68
  | Command | Description |
68
69
  | ---------------------------------------------------------- | ------------------------------------------------------ |
69
70
  | `gitdirector console` | Open the interactive TUI dashboard |
70
- | `gitdirector link PATH [--discover]` | Link a repository or discover all under a path |
71
+ | `gitdirector link PATH [--discover]` | Link a repository or discover all under a path (no-op if none) |
71
72
  | `gitdirector unlink PATH\|NAME [--discover]` | Unlink a repository by path, name, or all under a path |
72
73
  | `gitdirector list` | List all tracked repositories with live status |
73
74
  | `gitdirector status` | Show repositories with staged/unstaged files |
74
75
  | `gitdirector pull [--yes]` | Pull latest changes for all tracked repositories |
75
76
  | `gitdirector cd NAME` | Open or switch to a tmux session for a repository |
76
77
  | `gitdirector autoclean` | Remove broken repository links from tracking |
78
+ | `gitdirector reset [--yes]` | Kill all sessions and panels, wipe ~/.gitdirector, and recreate the config |
77
79
  | `gitdirector info PATH\|NAME [--full]` | Show file statistics for a repository |
78
80
  | `gitdirector gd-tmux PATH\|NAME "command" [--description TEXT]` | Create a gd tmux session and run a command in it |
79
81
  | `gitdirector gd-capture SESSION [--lines N] [--full]` | Print the scrollback of a live gd tmux session |
@@ -88,6 +90,8 @@ gitdirector link /path/to/repo
88
90
  gitdirector link /path/to/folder --discover # recursively find and link all repos
89
91
  ```
90
92
 
93
+ `link --discover` succeeds without changes when the path contains no new repositories.
94
+
91
95
  ### console
92
96
 
93
97
  ```bash
@@ -104,17 +108,17 @@ Features:
104
108
  - `/` to filter the active tab
105
109
  - `s` to sort the active table
106
110
  - `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 push
111
+ - `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
112
  - `r` to refresh all statuses
109
113
  - Press `enter` on any repository to open an action menu:
110
114
  - **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
115
  - **Attach existing session** — switch to any already-running tmux session
113
116
  - **Launch AI agent** — open OpenCode, Claude Code, GitHub Copilot, Codex, or Pi in a new tmux session
114
117
  - **Remove session** — kill a running tmux session
118
+ - **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
119
  - **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
120
  - **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.
121
+ - **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
122
 
119
123
  ### unlink
120
124
 
@@ -223,6 +227,15 @@ gitdirector autoclean
223
227
 
224
228
  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
229
 
230
+ ### reset
231
+
232
+ ```bash
233
+ gitdirector reset # asks for confirmation
234
+ gitdirector reset --yes # skip the confirmation prompt
235
+ ```
236
+
237
+ 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.
238
+
226
239
  ### gd-capture
227
240
 
228
241
  ```bash
@@ -263,7 +276,7 @@ If you are an AI coding agent (Claude Code, OpenCode, GitHub Copilot, Codex, Pi,
263
276
 
264
277
  ## Configuration
265
278
 
266
- Config is stored at `~/.gitdirector/config.yaml`.
279
+ Main config is stored at `~/.gitdirector/config.yaml`:
267
280
 
268
281
  ```yaml
269
282
  repositories:
@@ -271,6 +284,11 @@ repositories:
271
284
  - /path/to/repo2
272
285
  max_workers: 10 # optional, valid range 1-32, default 10
273
286
  theme: rose-pine # optional, default rose-pine
287
+ ```
288
+
289
+ Secrets (GitHub credentials) are stored in a separate file at `~/.gitdirector/secrets.yaml`:
290
+
291
+ ```yaml
274
292
  github_username: your-github-username # optional
275
293
  github_PAT: github_pat_or_classic_pat # optional
276
294
  ```
@@ -281,7 +299,7 @@ GitDirector first runs git commands using your normal local git credentials. If
281
299
 
282
300
  - This only applies to HTTPS GitHub remotes.
283
301
  - SSH remotes still use your normal SSH key/agent setup.
284
- - The PAT is stored in plaintext in `~/.gitdirector/config.yaml`; use an appropriately scoped token and protect that file.
302
+ - The PAT is stored in plaintext in `~/.gitdirector/secrets.yaml`; use an appropriately scoped token and protect that file.
285
303
  - The PAT is not passed on the command line or shown in TUI command output.
286
304
 
287
305
  ### Available Themes
@@ -304,6 +322,12 @@ eval "$(gitdirector completion zsh)"
304
322
  gitdirector completion fish | source
305
323
  ```
306
324
 
325
+ 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:
326
+
327
+ ```bash
328
+ gitdirector completion zsh > "${fpath[1]}/_gitdirector"
329
+ ```
330
+
307
331
  ## License
308
332
 
309
333
  MIT
@@ -25,13 +25,14 @@ If you find GitDirector useful, please star this repository on GitHub, we need m
25
25
  | Command | Description |
26
26
  | ---------------------------------------------------------- | ------------------------------------------------------ |
27
27
  | `gitdirector console` | Open the interactive TUI dashboard |
28
- | `gitdirector link PATH [--discover]` | Link a repository or discover all under a path |
28
+ | `gitdirector link PATH [--discover]` | Link a repository or discover all under a path (no-op if none) |
29
29
  | `gitdirector unlink PATH\|NAME [--discover]` | Unlink a repository by path, name, or all under a path |
30
30
  | `gitdirector list` | List all tracked repositories with live status |
31
31
  | `gitdirector status` | Show repositories with staged/unstaged files |
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 |
@@ -46,6 +47,8 @@ gitdirector link /path/to/repo
46
47
  gitdirector link /path/to/folder --discover # recursively find and link all repos
47
48
  ```
48
49
 
50
+ `link --discover` succeeds without changes when the path contains no new repositories.
51
+
49
52
  ### console
50
53
 
51
54
  ```bash
@@ -62,17 +65,17 @@ Features:
62
65
  - `/` to filter the active tab
63
66
  - `s` to sort the active table
64
67
  - `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 push
68
+ - `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
69
  - `r` to refresh all statuses
67
70
  - Press `enter` on any repository to open an action menu:
68
71
  - **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
72
  - **Attach existing session** — switch to any already-running tmux session
71
73
  - **Launch AI agent** — open OpenCode, Claude Code, GitHub Copilot, Codex, or Pi in a new tmux session
72
74
  - **Remove session** — kill a running tmux session
75
+ - **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
76
  - **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
77
  - **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.
78
+ - **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
79
 
77
80
  ### unlink
78
81
 
@@ -181,6 +184,15 @@ gitdirector autoclean
181
184
 
182
185
  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
186
 
187
+ ### reset
188
+
189
+ ```bash
190
+ gitdirector reset # asks for confirmation
191
+ gitdirector reset --yes # skip the confirmation prompt
192
+ ```
193
+
194
+ 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.
195
+
184
196
  ### gd-capture
185
197
 
186
198
  ```bash
@@ -221,7 +233,7 @@ If you are an AI coding agent (Claude Code, OpenCode, GitHub Copilot, Codex, Pi,
221
233
 
222
234
  ## Configuration
223
235
 
224
- Config is stored at `~/.gitdirector/config.yaml`.
236
+ Main config is stored at `~/.gitdirector/config.yaml`:
225
237
 
226
238
  ```yaml
227
239
  repositories:
@@ -229,6 +241,11 @@ repositories:
229
241
  - /path/to/repo2
230
242
  max_workers: 10 # optional, valid range 1-32, default 10
231
243
  theme: rose-pine # optional, default rose-pine
244
+ ```
245
+
246
+ Secrets (GitHub credentials) are stored in a separate file at `~/.gitdirector/secrets.yaml`:
247
+
248
+ ```yaml
232
249
  github_username: your-github-username # optional
233
250
  github_PAT: github_pat_or_classic_pat # optional
234
251
  ```
@@ -239,7 +256,7 @@ GitDirector first runs git commands using your normal local git credentials. If
239
256
 
240
257
  - This only applies to HTTPS GitHub remotes.
241
258
  - SSH remotes still use your normal SSH key/agent setup.
242
- - The PAT is stored in plaintext in `~/.gitdirector/config.yaml`; use an appropriately scoped token and protect that file.
259
+ - The PAT is stored in plaintext in `~/.gitdirector/secrets.yaml`; use an appropriately scoped token and protect that file.
243
260
  - The PAT is not passed on the command line or shown in TUI command output.
244
261
 
245
262
  ### Available Themes
@@ -262,6 +279,12 @@ eval "$(gitdirector completion zsh)"
262
279
  gitdirector completion fish | source
263
280
  ```
264
281
 
282
+ 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:
283
+
284
+ ```bash
285
+ gitdirector completion zsh > "${fpath[1]}/_gitdirector"
286
+ ```
287
+
265
288
  ## License
266
289
 
267
290
  MIT
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "gitdirector"
7
- version = "1.7.0"
7
+ version = "1.7.3"
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" }
@@ -37,6 +37,7 @@ dependencies = [
37
37
  "textual>=8.2.1",
38
38
  "tiktoken>=0.5.0",
39
39
  "pyte>=0.8.2",
40
+ "pathspec>=1.0.0",
40
41
  ]
41
42
 
42
43
  [project.optional-dependencies]
@@ -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)
@@ -18,6 +18,7 @@ from __future__ import annotations
18
18
  import click
19
19
 
20
20
  from ..integrations.tmux import capture_pane
21
+ from ..integrations.tmux.core import _parse_gd_session_name
21
22
 
22
23
 
23
24
  def _resolve_session_name(name: str) -> str:
@@ -26,8 +27,6 @@ def _resolve_session_name(name: str) -> str:
26
27
  gd-capture refuses anything that doesn't match the ``gd/{repo}/{purpose}/{N}``
27
28
  shape — that way a typo can't be silently routed to the wrong session.
28
29
  """
29
- from ..integrations.tmux import _parse_gd_session_name
30
-
31
30
  parsed = _parse_gd_session_name(name)
32
31
  if parsed is None:
33
32
  raise click.BadParameter(
@@ -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)
@@ -2,7 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import click
4
4
 
5
- from ..integrations.tmux import _parse_gd_session_name, send_key_to_session, send_text_to_session
5
+ from ..integrations.tmux import send_key_to_session, send_text_to_session
6
+ from ..integrations.tmux.core import _parse_gd_session_name
6
7
 
7
8
  SUPPORTED_KEYS = ("C-c",)
8
9
 
@@ -3,7 +3,6 @@ from pathlib import Path
3
3
  import click
4
4
 
5
5
  from ..manager import RepositoryManager
6
- from ..storage import normalize_repository_path
7
6
  from . import console
8
7
  from .completion import complete_repository_names
9
8
 
@@ -17,24 +16,12 @@ def _resolve_repo(target: str) -> Path | None:
17
16
  or ambiguous targets.
18
17
  """
19
18
  manager = RepositoryManager()
20
- candidate = Path(target)
21
- is_path_like = (
22
- "/" in target
23
- or "\\" in target
24
- or target in (".", "..")
25
- or candidate.is_absolute()
26
- or target.startswith("~")
27
- or candidate.exists()
28
- )
29
-
30
- if is_path_like:
31
- normalized = normalize_repository_path(candidate)
32
- if manager.config.has_repository(normalized):
33
- return normalized
34
- console.print(f"\n [red]No tracked repository at path: {normalized}[/red]\n")
19
+ repo_path, matches, path_attempted = manager.resolve_repository_target(target)
20
+ if repo_path is not None:
21
+ return repo_path
22
+ if path_attempted:
23
+ console.print(f"\n [red]No tracked repository at path: {target}[/red]\n")
35
24
  return None
36
-
37
- matches = [r for r in manager.config.repositories if r.name == target]
38
25
  if not matches:
39
26
  console.print(f"\n [red]No tracked repository named: {target}[/red]\n")
40
27
  return None
@@ -45,7 +32,7 @@ def _resolve_repo(target: str) -> Path | None:
45
32
  f"{paths_list}\n"
46
33
  )
47
34
  return None
48
- return matches[0]
35
+ return None
49
36
 
50
37
 
51
38
  def register(cli: click.Group):
@@ -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
 
@@ -25,7 +26,10 @@ def show_help():
25
26
  cmd_table.add_column("desc", style="dim")
26
27
 
27
28
  for cmd, desc in [
28
- ("link PATH [--discover]", "Link a repository or discover all repos under a path"),
29
+ (
30
+ "link PATH [--discover]",
31
+ "Link a repository or discover all repos under a path (no-op if none)",
32
+ ),
29
33
  ("unlink PATH|NAME [--discover]", "Unlink a repository or all repos under a path"),
30
34
  ("list", "List all tracked repositories"),
31
35
  ("status", "Show status summary and per-repo details"),
@@ -33,6 +37,7 @@ def show_help():
33
37
  ("cd NAME", "Open or switch to a tmux session for a repository"),
34
38
  ("console", "Interactive TUI for browsing and opening repositories"),
35
39
  ("autoclean", "Remove broken repository links from tracking"),
40
+ ("reset [--yes]", "Kill all sessions and panels, wipe ~/.gitdirector, recreate config"),
36
41
  ("info PATH|NAME [--full]", "Show file statistics for a repository"),
37
42
  (
38
43
  'gd-tmux PATH|NAME "cmd" [--description "..."]',
@@ -42,11 +47,11 @@ def show_help():
42
47
  "gd-capture SESSION [--lines N] [--full]",
43
48
  "Print the current scrollback of a live gd tmux session",
44
49
  ),
45
- ("gd-send SESSION [TEXT] [--enter|--key C-c]", "Send text or C-c to a gd session"),
50
+ ("gd-send SESSION [TEXT] [--enter | --key C-c]", "Send text or C-c to a gd session"),
46
51
  ("completion SHELL", "Print shell completion setup for bash, zsh, or fish"),
47
52
  ("help", "Show this help message"),
48
53
  ]:
49
- cmd_table.add_row(cmd, desc)
54
+ cmd_table.add_row(Text(cmd), desc)
50
55
 
51
56
  console.print(cmd_table)
52
57
 
@@ -46,29 +46,20 @@ def register(cli: click.Group):
46
46
  def info(target: str, full: bool):
47
47
  """Show file statistics for a repository."""
48
48
  manager = RepositoryManager()
49
- candidate_path = Path(target).expanduser()
50
-
51
- if candidate_path.is_dir() and (candidate_path / ".git").is_dir():
52
- repo_path = candidate_path.resolve()
53
- else:
54
- repos = manager.config.repositories
55
- target_lower = target.lower()
56
- exact = [r for r in repos if r.name.lower() == target_lower]
57
- if exact:
58
- matches = exact
59
- else:
60
- matches = [r for r in repos if target_lower in r.name.lower()]
61
-
49
+ repo_path, matches, _path_attempted = manager.resolve_repository_target(
50
+ target,
51
+ allow_untracked_git_path=True,
52
+ fuzzy_names=True,
53
+ )
54
+ if repo_path is None:
62
55
  if not matches:
63
56
  console.print(f"\n [red]Repository '{target}' not found[/red]\n")
64
57
  raise SystemExit(1)
65
- if len(matches) > 1:
66
- console.print(f"\n [red]Multiple repositories match '{target}':[/red]")
67
- for m in matches:
68
- console.print(f" {m}")
69
- console.print()
70
- raise SystemExit(1)
71
- repo_path = matches[0]
58
+ console.print(f"\n [red]Multiple repositories match '{target}':[/red]")
59
+ for match in matches:
60
+ console.print(f" {match}")
61
+ console.print()
62
+ raise SystemExit(1)
72
63
 
73
64
  result = gather_repo_info(repo_path, full=full)
74
65
  _render_info_cli(result, repo_path.name, repo_path)
@@ -28,6 +28,21 @@ def register(cli: click.Group):
28
28
  else:
29
29
  for repo_path in repos:
30
30
  console.print(f" [green]+[/green] {repo_path}")
31
+ elif (
32
+ discover
33
+ and not repos
34
+ and message
35
+ in {
36
+ "No git repositories found",
37
+ "No new repositories found",
38
+ }
39
+ ):
40
+ console.print(f" {message}")
41
+ for repo_path in skipped:
42
+ console.print(
43
+ f" [dim yellow]\\[skipped][/dim yellow] "
44
+ f"[bright_black]{repo_path}[/bright_black]"
45
+ )
31
46
  else:
32
47
  console.print(f" [red]{message}[/red]")
33
48
  console.print()
@@ -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
@@ -13,7 +13,7 @@ from .constants import (
13
13
  _changes_label,
14
14
  _changes_sort_key,
15
15
  )
16
- from .panel_view import PanelViewScreen, PaneWidget
16
+ from .panel_view import PaneWidget
17
17
  from .panels import Panel, PanelStore
18
18
  from .screens import SortMenuScreen
19
19
  from .screens.diff import DiffReviewScreen
@@ -51,7 +51,6 @@ __all__ = [
51
51
  "Panel",
52
52
  "PanelActionMenuScreen",
53
53
  "PanelStore",
54
- "PanelViewScreen",
55
54
  "_PANELS_SORT_COLUMN_NAMES",
56
55
  "PullLoadingScreen",
57
56
  "PullResultScreen",