gitdirector 1.2.0__tar.gz → 1.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.
- {gitdirector-1.2.0 → gitdirector-1.4.0}/PKG-INFO +57 -29
- {gitdirector-1.2.0 → gitdirector-1.4.0}/README.md +49 -20
- {gitdirector-1.2.0 → gitdirector-1.4.0}/pyproject.toml +15 -21
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/cli.py +14 -4
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/autoclean.py +6 -19
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/cd.py +2 -2
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/help.py +3 -2
- gitdirector-1.4.0/src/gitdirector/commands/info.py +73 -0
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/listt.py +15 -2
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/pull.py +29 -5
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/status.py +11 -3
- gitdirector-1.4.0/src/gitdirector/commands/tui/__init__.py +62 -0
- gitdirector-1.4.0/src/gitdirector/commands/tui/app.py +683 -0
- gitdirector-1.4.0/src/gitdirector/commands/tui/app_panels.py +364 -0
- gitdirector-1.4.0/src/gitdirector/commands/tui/app_repos.py +211 -0
- gitdirector-1.4.0/src/gitdirector/commands/tui/app_sessions.py +219 -0
- gitdirector-1.4.0/src/gitdirector/commands/tui/app_ui.py +395 -0
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/tui/constants.py +35 -3
- gitdirector-1.4.0/src/gitdirector/commands/tui/panel_view.py +469 -0
- gitdirector-1.4.0/src/gitdirector/commands/tui/panels.py +658 -0
- gitdirector-1.4.0/src/gitdirector/commands/tui/screens.py +1805 -0
- gitdirector-1.4.0/src/gitdirector/commands/tui/terminal_widget.py +430 -0
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/unlink.py +1 -1
- gitdirector-1.4.0/src/gitdirector/config.py +188 -0
- gitdirector-1.4.0/src/gitdirector/info.py +298 -0
- gitdirector-1.4.0/src/gitdirector/integrations/tmux/__init__.py +5 -0
- gitdirector-1.4.0/src/gitdirector/integrations/tmux/core.py +614 -0
- gitdirector-1.4.0/src/gitdirector/integrations/tmux/monitor.py +477 -0
- gitdirector-1.4.0/src/gitdirector/integrations/tmux/panels.py +800 -0
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/manager.py +28 -45
- gitdirector-1.4.0/src/gitdirector/repo.py +341 -0
- gitdirector-1.4.0/src/gitdirector/storage.py +87 -0
- gitdirector-1.4.0/src/gitdirector/ui_theme.py +89 -0
- gitdirector-1.2.0/src/gitdirector/commands/tui/__init__.py +0 -32
- gitdirector-1.2.0/src/gitdirector/commands/tui/app.py +0 -701
- gitdirector-1.2.0/src/gitdirector/commands/tui/screens.py +0 -318
- gitdirector-1.2.0/src/gitdirector/config.py +0 -81
- gitdirector-1.2.0/src/gitdirector/integrations/tmux.py +0 -118
- gitdirector-1.2.0/src/gitdirector/repo.py +0 -199
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/__init__.py +0 -0
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/__init__.py +0 -0
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/commands/link.py +0 -0
- {gitdirector-1.2.0 → gitdirector-1.4.0}/src/gitdirector/integrations/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: gitdirector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
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
|
|
@@ -11,7 +11,6 @@ Classifier: Intended Audience :: Developers
|
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
12
|
Classifier: Programming Language :: Python
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -22,19 +21,19 @@ Classifier: Topic :: Utilities
|
|
|
22
21
|
Requires-Dist: pyyaml>=6.0
|
|
23
22
|
Requires-Dist: click>=8.1.0
|
|
24
23
|
Requires-Dist: rich>=12.0
|
|
25
|
-
Requires-Dist: libtmux>=0.46.2
|
|
26
24
|
Requires-Dist: textual>=8.2.1
|
|
27
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: tiktoken>=0.5.0
|
|
26
|
+
Requires-Dist: pyte>=0.8.2
|
|
27
|
+
Requires-Dist: black>=23.0 ; extra == 'dev'
|
|
28
28
|
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
|
|
29
30
|
Requires-Dist: pytest-cov>=4.0 ; extra == 'dev'
|
|
30
|
-
Requires-Dist:
|
|
31
|
+
Requires-Dist: pytest-mock>=3.0 ; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-xdist>=3.8.0 ; extra == 'dev'
|
|
31
33
|
Requires-Dist: ruff>=0.1.0 ; extra == 'dev'
|
|
32
|
-
Requires-Dist: mypy>=1.0 ; extra == 'dev'
|
|
33
|
-
Requires-Dist: build>=1.0 ; extra == 'dev'
|
|
34
|
-
Requires-Dist: twine>=4.0 ; extra == 'dev'
|
|
35
34
|
Maintainer: Anito Anto
|
|
36
35
|
Maintainer-email: Anito Anto <49053859+anitoanto@users.noreply.github.com>
|
|
37
|
-
Requires-Python: >=3.
|
|
36
|
+
Requires-Python: >=3.10
|
|
38
37
|
Project-URL: Homepage, https://github.com/anitoanto/gitdirector
|
|
39
38
|
Project-URL: Repository, https://github.com/anitoanto/gitdirector.git
|
|
40
39
|
Project-URL: Issues, https://github.com/anitoanto/gitdirector/issues
|
|
@@ -58,23 +57,26 @@ GitDirector gives you a single cockpit for all of it. See every repo's status, D
|
|
|
58
57
|
pip install gitdirector
|
|
59
58
|
```
|
|
60
59
|
|
|
60
|
+
Requires Python 3.10+.
|
|
61
|
+
|
|
61
62
|
## Support
|
|
62
63
|
|
|
63
64
|
If you find GitDirector useful, please star this repository on GitHub, we need more stars to qualify for inclusion in Homebrew. Your support helps a lot, thank you!
|
|
64
65
|
|
|
65
66
|
## Usage
|
|
66
67
|
|
|
67
|
-
| Command
|
|
68
|
-
|
|
|
69
|
-
| `gitdirector console`
|
|
70
|
-
| `gitdirector link PATH [--discover]`
|
|
68
|
+
| Command | Description |
|
|
69
|
+
| -------------------------------------------- | ------------------------------------------------------ |
|
|
70
|
+
| `gitdirector console` | Open the interactive TUI dashboard |
|
|
71
|
+
| `gitdirector link PATH [--discover]` | Link a repository or discover all under a path |
|
|
71
72
|
| `gitdirector unlink PATH\|NAME [--discover]` | Unlink a repository by path, name, or all under a path |
|
|
72
|
-
| `gitdirector list`
|
|
73
|
-
| `gitdirector status`
|
|
74
|
-
| `gitdirector pull`
|
|
75
|
-
| `gitdirector cd NAME`
|
|
76
|
-
| `gitdirector autoclean links\|sessions`
|
|
77
|
-
| `gitdirector
|
|
73
|
+
| `gitdirector list` | List all tracked repositories with live status |
|
|
74
|
+
| `gitdirector status` | Show repositories with staged/unstaged files |
|
|
75
|
+
| `gitdirector pull` | Pull latest changes for all tracked repositories |
|
|
76
|
+
| `gitdirector cd NAME` | Open or switch to a tmux session for a repository |
|
|
77
|
+
| `gitdirector autoclean links\|sessions` | Clean broken links or stale tmux sessions |
|
|
78
|
+
| `gitdirector info PATH\|NAME [--full]` | Show file statistics for a repository |
|
|
79
|
+
| `gitdirector help` | Show help |
|
|
78
80
|
|
|
79
81
|
### link
|
|
80
82
|
|
|
@@ -94,14 +96,16 @@ Opens a full interactive TUI dashboard.
|
|
|
94
96
|
Features:
|
|
95
97
|
|
|
96
98
|
- Live table with sync state, branch, changes, last commit, and active tmux sessions
|
|
99
|
+
- `j`/`k` or arrow keys to navigate
|
|
97
100
|
- `/` to filter repositories by name or path
|
|
98
101
|
- `s` to cycle sort by any column
|
|
102
|
+
- `i` to show repository info (file count, lines, tokens, max depth, top file types)
|
|
99
103
|
- `r` to refresh all statuses
|
|
100
104
|
- Press `enter` on any repository to open an action menu:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
- **New tmux session** — create and attach a session for the repository
|
|
106
|
+
- **Attach existing session** — switch to any already-running tmux session
|
|
107
|
+
- **Launch AI agent** — open OpenCode, Claude Code, GitHub Copilot, or Codex in a new tmux session
|
|
108
|
+
- **Remove session** — kill a running tmux session
|
|
105
109
|
|
|
106
110
|
### unlink
|
|
107
111
|
|
|
@@ -113,6 +117,25 @@ gitdirector unlink /path/to/folder --discover # unlink all repos under a path
|
|
|
113
117
|
|
|
114
118
|
If multiple tracked repositories share the same name, `gitdirector` will refuse and list the conflicting paths so you can use the full path instead.
|
|
115
119
|
|
|
120
|
+
### info
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
gitdirector info /path/to/repo # by path
|
|
124
|
+
gitdirector info my-repo # by name
|
|
125
|
+
gitdirector info my-repo --full # show all file extensions
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Shows file statistics for a repository:
|
|
129
|
+
|
|
130
|
+
- Total file count, line count, token count, and max directory depth
|
|
131
|
+
- Top 10 file types by count with line and token breakdowns (use `--full` to show all)
|
|
132
|
+
- Files without extensions grouped as `(no ext)`
|
|
133
|
+
- Remaining types grouped as `others`
|
|
134
|
+
- Binary files show `-` for lines and tokens
|
|
135
|
+
- All operations respect `.gitignore` at every level
|
|
136
|
+
|
|
137
|
+
Also available in the TUI console by pressing `i` on a selected repository.
|
|
138
|
+
|
|
116
139
|
### list
|
|
117
140
|
|
|
118
141
|
Displays a live table of all tracked repositories with:
|
|
@@ -124,7 +147,7 @@ Displays a live table of all tracked repositories with:
|
|
|
124
147
|
- Tracked file size
|
|
125
148
|
- Path
|
|
126
149
|
|
|
127
|
-
Checks run concurrently (default: 10 workers).
|
|
150
|
+
Checks run concurrently (default: 10 workers, configurable from 1 to 32).
|
|
128
151
|
|
|
129
152
|
### status
|
|
130
153
|
|
|
@@ -132,7 +155,7 @@ Shows repositories with uncommitted changes (staged and/or unstaged files). Prin
|
|
|
132
155
|
|
|
133
156
|
### pull
|
|
134
157
|
|
|
135
|
-
Pulls all tracked repositories concurrently using fast-forward only (`git pull --ff-only
|
|
158
|
+
Pulls all tracked repositories concurrently using fast-forward only on each repository's current branch (`git pull --ff-only origin <current-branch>`). Reports success or failure per repository.
|
|
136
159
|
|
|
137
160
|
### cd
|
|
138
161
|
|
|
@@ -157,14 +180,19 @@ Config is stored at `~/.gitdirector/config.yaml`.
|
|
|
157
180
|
|
|
158
181
|
```yaml
|
|
159
182
|
repositories:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
max_workers: 10
|
|
183
|
+
- /path/to/repo1
|
|
184
|
+
- /path/to/repo2
|
|
185
|
+
max_workers: 10 # optional, valid range 1-32, default 10
|
|
186
|
+
theme: rose-pine # optional, default rose-pine
|
|
163
187
|
```
|
|
164
188
|
|
|
189
|
+
### Available Themes
|
|
190
|
+
|
|
191
|
+
`textual-dark`, `textual-light`, `nord`, `gruvbox`, `catppuccin-mocha`, `textual-ansi`, `dracula`, `tokyo-night`, `monokai`, `flexoki`, `catppuccin-latte`, `catppuccin-frappe`, `catppuccin-macchiato`, `solarized-light`, `solarized-dark`, `rose-pine`, `rose-pine-moon`, `rose-pine-dawn`, `atom-one-dark`, `atom-one-light`
|
|
192
|
+
|
|
165
193
|
## Requirements
|
|
166
194
|
|
|
167
|
-
- Python 3.
|
|
195
|
+
- Python 3.10+
|
|
168
196
|
- Git
|
|
169
197
|
- [tmux](https://github.com/tmux/tmux) ≥ 3.2a (for `gitdirector cd`)
|
|
170
198
|
|
|
@@ -14,23 +14,26 @@ GitDirector gives you a single cockpit for all of it. See every repo's status, D
|
|
|
14
14
|
pip install gitdirector
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
Requires Python 3.10+.
|
|
18
|
+
|
|
17
19
|
## Support
|
|
18
20
|
|
|
19
21
|
If you find GitDirector useful, please star this repository on GitHub, we need more stars to qualify for inclusion in Homebrew. Your support helps a lot, thank you!
|
|
20
22
|
|
|
21
23
|
## Usage
|
|
22
24
|
|
|
23
|
-
| Command
|
|
24
|
-
|
|
|
25
|
-
| `gitdirector console`
|
|
26
|
-
| `gitdirector link PATH [--discover]`
|
|
25
|
+
| Command | Description |
|
|
26
|
+
| -------------------------------------------- | ------------------------------------------------------ |
|
|
27
|
+
| `gitdirector console` | Open the interactive TUI dashboard |
|
|
28
|
+
| `gitdirector link PATH [--discover]` | Link a repository or discover all under a path |
|
|
27
29
|
| `gitdirector unlink PATH\|NAME [--discover]` | Unlink a repository by path, name, or all under a path |
|
|
28
|
-
| `gitdirector list`
|
|
29
|
-
| `gitdirector status`
|
|
30
|
-
| `gitdirector pull`
|
|
31
|
-
| `gitdirector cd NAME`
|
|
32
|
-
| `gitdirector autoclean links\|sessions`
|
|
33
|
-
| `gitdirector
|
|
30
|
+
| `gitdirector list` | List all tracked repositories with live status |
|
|
31
|
+
| `gitdirector status` | Show repositories with staged/unstaged files |
|
|
32
|
+
| `gitdirector pull` | Pull latest changes for all tracked repositories |
|
|
33
|
+
| `gitdirector cd NAME` | Open or switch to a tmux session for a repository |
|
|
34
|
+
| `gitdirector autoclean links\|sessions` | Clean broken links or stale tmux sessions |
|
|
35
|
+
| `gitdirector info PATH\|NAME [--full]` | Show file statistics for a repository |
|
|
36
|
+
| `gitdirector help` | Show help |
|
|
34
37
|
|
|
35
38
|
### link
|
|
36
39
|
|
|
@@ -50,14 +53,16 @@ Opens a full interactive TUI dashboard.
|
|
|
50
53
|
Features:
|
|
51
54
|
|
|
52
55
|
- Live table with sync state, branch, changes, last commit, and active tmux sessions
|
|
56
|
+
- `j`/`k` or arrow keys to navigate
|
|
53
57
|
- `/` to filter repositories by name or path
|
|
54
58
|
- `s` to cycle sort by any column
|
|
59
|
+
- `i` to show repository info (file count, lines, tokens, max depth, top file types)
|
|
55
60
|
- `r` to refresh all statuses
|
|
56
61
|
- Press `enter` on any repository to open an action menu:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
- **New tmux session** — create and attach a session for the repository
|
|
63
|
+
- **Attach existing session** — switch to any already-running tmux session
|
|
64
|
+
- **Launch AI agent** — open OpenCode, Claude Code, GitHub Copilot, or Codex in a new tmux session
|
|
65
|
+
- **Remove session** — kill a running tmux session
|
|
61
66
|
|
|
62
67
|
### unlink
|
|
63
68
|
|
|
@@ -69,6 +74,25 @@ gitdirector unlink /path/to/folder --discover # unlink all repos under a path
|
|
|
69
74
|
|
|
70
75
|
If multiple tracked repositories share the same name, `gitdirector` will refuse and list the conflicting paths so you can use the full path instead.
|
|
71
76
|
|
|
77
|
+
### info
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
gitdirector info /path/to/repo # by path
|
|
81
|
+
gitdirector info my-repo # by name
|
|
82
|
+
gitdirector info my-repo --full # show all file extensions
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Shows file statistics for a repository:
|
|
86
|
+
|
|
87
|
+
- Total file count, line count, token count, and max directory depth
|
|
88
|
+
- Top 10 file types by count with line and token breakdowns (use `--full` to show all)
|
|
89
|
+
- Files without extensions grouped as `(no ext)`
|
|
90
|
+
- Remaining types grouped as `others`
|
|
91
|
+
- Binary files show `-` for lines and tokens
|
|
92
|
+
- All operations respect `.gitignore` at every level
|
|
93
|
+
|
|
94
|
+
Also available in the TUI console by pressing `i` on a selected repository.
|
|
95
|
+
|
|
72
96
|
### list
|
|
73
97
|
|
|
74
98
|
Displays a live table of all tracked repositories with:
|
|
@@ -80,7 +104,7 @@ Displays a live table of all tracked repositories with:
|
|
|
80
104
|
- Tracked file size
|
|
81
105
|
- Path
|
|
82
106
|
|
|
83
|
-
Checks run concurrently (default: 10 workers).
|
|
107
|
+
Checks run concurrently (default: 10 workers, configurable from 1 to 32).
|
|
84
108
|
|
|
85
109
|
### status
|
|
86
110
|
|
|
@@ -88,7 +112,7 @@ Shows repositories with uncommitted changes (staged and/or unstaged files). Prin
|
|
|
88
112
|
|
|
89
113
|
### pull
|
|
90
114
|
|
|
91
|
-
Pulls all tracked repositories concurrently using fast-forward only (`git pull --ff-only
|
|
115
|
+
Pulls all tracked repositories concurrently using fast-forward only on each repository's current branch (`git pull --ff-only origin <current-branch>`). Reports success or failure per repository.
|
|
92
116
|
|
|
93
117
|
### cd
|
|
94
118
|
|
|
@@ -113,14 +137,19 @@ Config is stored at `~/.gitdirector/config.yaml`.
|
|
|
113
137
|
|
|
114
138
|
```yaml
|
|
115
139
|
repositories:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
max_workers: 10
|
|
140
|
+
- /path/to/repo1
|
|
141
|
+
- /path/to/repo2
|
|
142
|
+
max_workers: 10 # optional, valid range 1-32, default 10
|
|
143
|
+
theme: rose-pine # optional, default rose-pine
|
|
119
144
|
```
|
|
120
145
|
|
|
146
|
+
### Available Themes
|
|
147
|
+
|
|
148
|
+
`textual-dark`, `textual-light`, `nord`, `gruvbox`, `catppuccin-mocha`, `textual-ansi`, `dracula`, `tokyo-night`, `monokai`, `flexoki`, `catppuccin-latte`, `catppuccin-frappe`, `catppuccin-macchiato`, `solarized-light`, `solarized-dark`, `rose-pine`, `rose-pine-moon`, `rose-pine-dawn`, `atom-one-dark`, `atom-one-light`
|
|
149
|
+
|
|
121
150
|
## Requirements
|
|
122
151
|
|
|
123
|
-
- Python 3.
|
|
152
|
+
- Python 3.10+
|
|
124
153
|
- Git
|
|
125
154
|
- [tmux](https://github.com/tmux/tmux) ≥ 3.2a (for `gitdirector cd`)
|
|
126
155
|
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitdirector"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.0"
|
|
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" }
|
|
@@ -14,7 +14,7 @@ authors = [
|
|
|
14
14
|
maintainers = [
|
|
15
15
|
{ name = "Anito Anto", email = "49053859+anitoanto@users.noreply.github.com" }
|
|
16
16
|
]
|
|
17
|
-
requires-python = ">=3.
|
|
17
|
+
requires-python = ">=3.10"
|
|
18
18
|
keywords = ["git", "repository", "manager", "cli", "synchronization", "batch"]
|
|
19
19
|
classifiers = [
|
|
20
20
|
"Environment :: Console",
|
|
@@ -22,7 +22,6 @@ classifiers = [
|
|
|
22
22
|
"Operating System :: OS Independent",
|
|
23
23
|
"Programming Language :: Python",
|
|
24
24
|
"Programming Language :: Python :: 3",
|
|
25
|
-
"Programming Language :: Python :: 3.9",
|
|
26
25
|
"Programming Language :: Python :: 3.10",
|
|
27
26
|
"Programming Language :: Python :: 3.11",
|
|
28
27
|
"Programming Language :: Python :: 3.12",
|
|
@@ -35,20 +34,20 @@ dependencies = [
|
|
|
35
34
|
"pyyaml>=6.0",
|
|
36
35
|
"click>=8.1.0",
|
|
37
36
|
"rich>=12.0",
|
|
38
|
-
"libtmux>=0.46.2",
|
|
39
37
|
"textual>=8.2.1",
|
|
40
|
-
"
|
|
38
|
+
"tiktoken>=0.5.0",
|
|
39
|
+
"pyte>=0.8.2",
|
|
41
40
|
]
|
|
42
41
|
|
|
43
42
|
[project.optional-dependencies]
|
|
44
43
|
dev = [
|
|
44
|
+
"black>=23.0",
|
|
45
45
|
"pytest>=7.0",
|
|
46
|
+
"pytest-asyncio>=0.23",
|
|
46
47
|
"pytest-cov>=4.0",
|
|
47
|
-
"
|
|
48
|
+
"pytest-mock>=3.0",
|
|
49
|
+
"pytest-xdist>=3.8.0",
|
|
48
50
|
"ruff>=0.1.0",
|
|
49
|
-
"mypy>=1.0",
|
|
50
|
-
"build>=1.0",
|
|
51
|
-
"twine>=4.0",
|
|
52
51
|
]
|
|
53
52
|
|
|
54
53
|
[project.urls]
|
|
@@ -65,36 +64,31 @@ managed = true
|
|
|
65
64
|
|
|
66
65
|
[dependency-groups]
|
|
67
66
|
dev = [
|
|
67
|
+
"black>=23.0",
|
|
68
68
|
"pytest>=7.0",
|
|
69
69
|
"pytest-asyncio>=0.23",
|
|
70
70
|
"pytest-cov>=4.0",
|
|
71
71
|
"pytest-mock>=3.0",
|
|
72
|
-
"
|
|
72
|
+
"pytest-xdist>=3.8.0",
|
|
73
73
|
"ruff>=0.1.0",
|
|
74
|
-
"mypy>=1.0",
|
|
75
74
|
]
|
|
76
75
|
|
|
77
76
|
[tool.black]
|
|
78
77
|
line-length = 100
|
|
79
|
-
target-version = ["
|
|
78
|
+
target-version = ["py310", "py311", "py312"]
|
|
80
79
|
|
|
81
80
|
[tool.ruff]
|
|
82
81
|
line-length = 100
|
|
83
|
-
target-version = "
|
|
82
|
+
target-version = "py310"
|
|
84
83
|
|
|
85
84
|
[tool.ruff.lint]
|
|
86
85
|
select = ["E", "F", "W", "I"]
|
|
86
|
+
ignore = ["E501"]
|
|
87
87
|
|
|
88
88
|
[tool.ruff.lint.per-file-ignores]
|
|
89
|
-
"__init__.py" = ["F401"]
|
|
90
|
-
|
|
91
|
-
[tool.mypy]
|
|
92
|
-
python_version = "3.12"
|
|
93
|
-
warn_return_any = true
|
|
94
|
-
warn_unused_configs = true
|
|
95
|
-
disallow_untyped_defs = false
|
|
89
|
+
"__init__.py" = ["F401", "F403"]
|
|
96
90
|
|
|
97
91
|
[tool.pytest.ini_options]
|
|
98
92
|
testpaths = ["tests"]
|
|
99
|
-
addopts = "--cov=src/gitdirector --cov-report=term-missing"
|
|
93
|
+
addopts = "-n auto --cov=src/gitdirector --cov-report=term-missing"
|
|
100
94
|
asyncio_mode = "auto"
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
|
|
1
3
|
import click
|
|
2
4
|
|
|
3
5
|
from .commands import (
|
|
@@ -9,6 +11,7 @@ from .commands import (
|
|
|
9
11
|
cd,
|
|
10
12
|
console,
|
|
11
13
|
help,
|
|
14
|
+
info,
|
|
12
15
|
link,
|
|
13
16
|
listt,
|
|
14
17
|
pull,
|
|
@@ -29,7 +32,7 @@ __all__ = [
|
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
class _HelpGroup(click.Group):
|
|
32
|
-
def format_help(self, ctx,
|
|
35
|
+
def format_help(self, ctx, _formatter):
|
|
33
36
|
show_help()
|
|
34
37
|
|
|
35
38
|
|
|
@@ -49,14 +52,21 @@ cd.register(cli)
|
|
|
49
52
|
help.register(cli)
|
|
50
53
|
tui.register(cli)
|
|
51
54
|
autoclean.register(cli)
|
|
55
|
+
info.register(cli)
|
|
52
56
|
|
|
53
57
|
|
|
54
58
|
def main():
|
|
55
59
|
try:
|
|
56
60
|
cli()
|
|
57
|
-
except
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
except (
|
|
62
|
+
click.ClickException,
|
|
63
|
+
OSError,
|
|
64
|
+
RuntimeError,
|
|
65
|
+
ValueError,
|
|
66
|
+
subprocess.SubprocessError,
|
|
67
|
+
) as exc:
|
|
68
|
+
console.print(f"\n [red]Error:[/red] {str(exc)}\n")
|
|
69
|
+
raise SystemExit(1) from exc
|
|
60
70
|
|
|
61
71
|
|
|
62
72
|
if __name__ == "__main__":
|
|
@@ -1,29 +1,16 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
|
|
3
1
|
import click
|
|
4
2
|
|
|
5
3
|
from ..config import Config
|
|
4
|
+
from ..integrations.tmux import _list_sessions, kill_tmux_session
|
|
6
5
|
from . import console
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
def _list_gd_sessions() -> list[str]:
|
|
10
|
-
|
|
11
|
-
result = subprocess.run(
|
|
12
|
-
["tmux", "list-sessions", "-F", "#{session_name}"],
|
|
13
|
-
capture_output=True,
|
|
14
|
-
text=True,
|
|
15
|
-
)
|
|
16
|
-
if result.returncode != 0:
|
|
17
|
-
return []
|
|
18
|
-
return sorted(s for s in result.stdout.strip().split("\n") if s.startswith("gd-"))
|
|
9
|
+
return [s for s in _list_sessions() if s.startswith("gd/")]
|
|
19
10
|
|
|
20
11
|
|
|
21
12
|
def _kill_session(name: str) -> bool:
|
|
22
|
-
|
|
23
|
-
["tmux", "kill-session", "-t", name],
|
|
24
|
-
capture_output=True,
|
|
25
|
-
)
|
|
26
|
-
return result.returncode == 0
|
|
13
|
+
return kill_tmux_session(name)
|
|
27
14
|
|
|
28
15
|
|
|
29
16
|
def register(cli: click.Group):
|
|
@@ -49,7 +36,7 @@ def _autoclean_links():
|
|
|
49
36
|
console.print()
|
|
50
37
|
console.print(f" Found [yellow]{len(broken)}[/yellow] broken link(s):\n")
|
|
51
38
|
for p in broken:
|
|
52
|
-
console.print(f" [red]✕[/red] {p}")
|
|
39
|
+
console.print(f" [red]✕[/red] {p}", soft_wrap=True)
|
|
53
40
|
console.print()
|
|
54
41
|
|
|
55
42
|
if not click.confirm(" Remove these broken links?"):
|
|
@@ -77,7 +64,7 @@ def _autoclean_sessions():
|
|
|
77
64
|
console.print()
|
|
78
65
|
console.print(f" Found [yellow]{len(sessions)}[/yellow] gitdirector tmux session(s):\n")
|
|
79
66
|
for s in sessions:
|
|
80
|
-
console.print(f" [dim]•[/dim] {s}")
|
|
67
|
+
console.print(f" [dim]•[/dim] {s}", soft_wrap=True)
|
|
81
68
|
console.print()
|
|
82
69
|
|
|
83
70
|
if not click.confirm(f" Kill all {len(sessions)} session(s)?"):
|
|
@@ -91,7 +78,7 @@ def _autoclean_sessions():
|
|
|
91
78
|
if _kill_session(s):
|
|
92
79
|
killed += 1
|
|
93
80
|
else:
|
|
94
|
-
console.print(f" [red]Failed to kill:[/red] {s}")
|
|
81
|
+
console.print(f" [red]Failed to kill:[/red] {s}", soft_wrap=True)
|
|
95
82
|
|
|
96
83
|
console.print()
|
|
97
84
|
console.print(f" [green]Killed {killed} session(s).[/green]")
|
|
@@ -28,8 +28,8 @@ def register(cli: click.Group):
|
|
|
28
28
|
from ..integrations.tmux import open_in_tmux
|
|
29
29
|
except ImportError:
|
|
30
30
|
console.print(
|
|
31
|
-
"\n [red]
|
|
32
|
-
"
|
|
31
|
+
"\n [red]The tmux integration is unavailable for the cd command.[/red]\n"
|
|
32
|
+
" Reinstall gitdirector or check your installation.\n"
|
|
33
33
|
)
|
|
34
34
|
raise SystemExit(1)
|
|
35
35
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from rich.table import Table
|
|
3
3
|
|
|
4
|
-
from . import
|
|
4
|
+
from . import console, get_version
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def show_help():
|
|
@@ -25,13 +25,14 @@ def show_help():
|
|
|
25
25
|
|
|
26
26
|
for cmd, desc in [
|
|
27
27
|
("link PATH [--discover]", "Link a repository or discover all repos under a path"),
|
|
28
|
-
("unlink PATH [--discover]", "Unlink a repository or all repos under a path"),
|
|
28
|
+
("unlink PATH|NAME [--discover]", "Unlink a repository or all repos under a path"),
|
|
29
29
|
("list", "List all tracked repositories"),
|
|
30
30
|
("status", "Show status summary and per-repo details"),
|
|
31
31
|
("pull", "Pull latest changes for all tracked repositories"),
|
|
32
32
|
("cd NAME", "Open or switch to a tmux session for a repository"),
|
|
33
33
|
("console", "Interactive TUI for browsing and opening repositories"),
|
|
34
34
|
("autoclean links|sessions", "Clean broken links or stale tmux sessions"),
|
|
35
|
+
("info PATH|NAME [--full]", "Show file statistics for a repository"),
|
|
35
36
|
("help", "Show this help message"),
|
|
36
37
|
]:
|
|
37
38
|
cmd_table.add_row(cmd, desc)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from ..info import RepoInfoResult, gather_repo_info
|
|
6
|
+
from ..manager import RepositoryManager
|
|
7
|
+
from . import console
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _render_info_cli(result: RepoInfoResult, name: str, path: Path) -> None:
|
|
11
|
+
console.print()
|
|
12
|
+
console.print(f" [bold white]{name}[/bold white]")
|
|
13
|
+
console.print(f" [dim]{path}[/dim]")
|
|
14
|
+
console.print()
|
|
15
|
+
console.print(f" [dim]Files[/dim] [bold white]{result.total_files:,}[/bold white]")
|
|
16
|
+
console.print(f" [dim]Lines[/dim] [bold white]{result.total_lines:,}[/bold white]")
|
|
17
|
+
console.print(f" [dim]Tokens[/dim] [bold white]{result.total_tokens:,}[/bold white]")
|
|
18
|
+
console.print(f" [dim]Max Depth[/dim] [bold white]{result.max_depth}[/bold white]")
|
|
19
|
+
console.print()
|
|
20
|
+
|
|
21
|
+
if result.file_types:
|
|
22
|
+
console.print(
|
|
23
|
+
f" [dim]{'EXTENSION':<12} {'FILES':>6} {'LINES':>8} {'TOKENS':>10}[/dim]"
|
|
24
|
+
)
|
|
25
|
+
for ft in result.file_types:
|
|
26
|
+
lines_str = f"{ft.line_count:,}" if ft.line_count is not None else "-"
|
|
27
|
+
tokens_str = f"{ft.token_count:,}" if ft.token_count is not None else "-"
|
|
28
|
+
console.print(
|
|
29
|
+
f" [cyan]{ft.extension:<12}[/cyan] [white]{ft.count:>6}[/white]"
|
|
30
|
+
f" [dim]{lines_str:>8}[/dim]"
|
|
31
|
+
f" [dim]{tokens_str:>10}[/dim]"
|
|
32
|
+
)
|
|
33
|
+
console.print()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def register(cli: click.Group):
|
|
37
|
+
@cli.command()
|
|
38
|
+
@click.argument("target", metavar="PATH|NAME")
|
|
39
|
+
@click.option(
|
|
40
|
+
"--full",
|
|
41
|
+
is_flag=True,
|
|
42
|
+
default=False,
|
|
43
|
+
help="Show all file extensions without the top 10 cap.",
|
|
44
|
+
)
|
|
45
|
+
def info(target: str, full: bool):
|
|
46
|
+
"""Show file statistics for a repository."""
|
|
47
|
+
manager = RepositoryManager()
|
|
48
|
+
candidate_path = Path(target).expanduser()
|
|
49
|
+
|
|
50
|
+
if candidate_path.is_dir() and (candidate_path / ".git").is_dir():
|
|
51
|
+
repo_path = candidate_path.resolve()
|
|
52
|
+
else:
|
|
53
|
+
repos = manager.config.repositories
|
|
54
|
+
target_lower = target.lower()
|
|
55
|
+
exact = [r for r in repos if r.name.lower() == target_lower]
|
|
56
|
+
if exact:
|
|
57
|
+
matches = exact
|
|
58
|
+
else:
|
|
59
|
+
matches = [r for r in repos if target_lower in r.name.lower()]
|
|
60
|
+
|
|
61
|
+
if not matches:
|
|
62
|
+
console.print(f"\n [red]Repository '{target}' not found[/red]\n")
|
|
63
|
+
raise SystemExit(1)
|
|
64
|
+
if len(matches) > 1:
|
|
65
|
+
console.print(f"\n [red]Multiple repositories match '{target}':[/red]")
|
|
66
|
+
for m in matches:
|
|
67
|
+
console.print(f" {m}")
|
|
68
|
+
console.print()
|
|
69
|
+
raise SystemExit(1)
|
|
70
|
+
repo_path = matches[0]
|
|
71
|
+
|
|
72
|
+
result = gather_repo_info(repo_path, full=full)
|
|
73
|
+
_render_info_cli(result, repo_path.name, repo_path)
|
|
@@ -5,6 +5,7 @@ from rich.live import Live
|
|
|
5
5
|
from rich.spinner import Spinner
|
|
6
6
|
|
|
7
7
|
from ..manager import RepositoryManager
|
|
8
|
+
from ..repo import RepositoryInfo, RepoStatus
|
|
8
9
|
from . import _build_repo_table, console
|
|
9
10
|
|
|
10
11
|
|
|
@@ -25,7 +26,13 @@ def register(cli: click.Group):
|
|
|
25
26
|
) as live:
|
|
26
27
|
with ThreadPoolExecutor(max_workers=manager.config.max_workers) as executor:
|
|
27
28
|
futures = {
|
|
28
|
-
executor.submit(
|
|
29
|
+
executor.submit(
|
|
30
|
+
manager.get_repository_status,
|
|
31
|
+
path,
|
|
32
|
+
fetch=True,
|
|
33
|
+
include_size=True,
|
|
34
|
+
): path
|
|
35
|
+
for path in paths
|
|
29
36
|
}
|
|
30
37
|
remaining = len(futures)
|
|
31
38
|
live.update(
|
|
@@ -33,7 +40,13 @@ def register(cli: click.Group):
|
|
|
33
40
|
)
|
|
34
41
|
for future in as_completed(futures):
|
|
35
42
|
remaining -= 1
|
|
36
|
-
|
|
43
|
+
path = futures[future]
|
|
44
|
+
try:
|
|
45
|
+
results.append(future.result())
|
|
46
|
+
except Exception as exc:
|
|
47
|
+
results.append(
|
|
48
|
+
RepositoryInfo(path, path.name, RepoStatus.UNKNOWN, None, str(exc))
|
|
49
|
+
)
|
|
37
50
|
done = len(results)
|
|
38
51
|
if remaining > 0:
|
|
39
52
|
live.update(
|