mud-git 1.1.6__tar.gz → 1.1.8__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 (42) hide show
  1. {mud_git-1.1.6 → mud_git-1.1.8}/.github/workflows/publish-aur.yaml +26 -1
  2. mud_git-1.1.8/.github/workflows/publish-pypi.yaml +59 -0
  3. mud_git-1.1.8/.github/workflows/test.yaml +38 -0
  4. {mud_git-1.1.6 → mud_git-1.1.8}/.gitignore +3 -0
  5. mud_git-1.1.8/AGENTS.md +78 -0
  6. {mud_git-1.1.6 → mud_git-1.1.8}/PKG-INFO +32 -11
  7. {mud_git-1.1.6 → mud_git-1.1.8}/README.md +29 -10
  8. {mud_git-1.1.6 → mud_git-1.1.8}/pyproject.toml +8 -0
  9. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/__init__.py +9 -1
  10. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/app.py +25 -9
  11. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/commands.py +3 -1
  12. mud_git-1.1.8/src/mud/completion.py +143 -0
  13. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/config.py +6 -2
  14. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/runner.py +3 -4
  15. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/settings.py +4 -2
  16. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/styles.py +0 -1
  17. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud_git.egg-info/PKG-INFO +32 -11
  18. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud_git.egg-info/SOURCES.txt +13 -1
  19. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud_git.egg-info/requires.txt +3 -0
  20. mud_git-1.1.8/src/mud_git.egg-info/scm_file_list.json +34 -0
  21. mud_git-1.1.8/src/mud_git.egg-info/scm_version.json +8 -0
  22. mud_git-1.1.8/tests/conftest.py +29 -0
  23. mud_git-1.1.8/tests/helpers.py +102 -0
  24. mud_git-1.1.8/tests/test_completion.py +232 -0
  25. mud_git-1.1.8/tests/test_config.py +93 -0
  26. mud_git-1.1.8/tests/test_display.py +71 -0
  27. mud_git-1.1.8/tests/test_filters.py +93 -0
  28. mud_git-1.1.8/tests/test_run.py +55 -0
  29. mud_git-1.1.8/tests/test_states.py +86 -0
  30. mud_git-1.1.6/.github/workflows/publish-pypi.yaml +0 -31
  31. mud_git-1.1.6/.github/workflows/test.yaml +0 -110
  32. {mud_git-1.1.6 → mud_git-1.1.8}/.editorconfig +0 -0
  33. {mud_git-1.1.6 → mud_git-1.1.8}/LICENSE +0 -0
  34. {mud_git-1.1.6 → mud_git-1.1.8}/PKGBUILD +0 -0
  35. {mud_git-1.1.6 → mud_git-1.1.8}/img.png +0 -0
  36. {mud_git-1.1.6 → mud_git-1.1.8}/requirements.txt +0 -0
  37. {mud_git-1.1.6 → mud_git-1.1.8}/setup.cfg +0 -0
  38. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/__main__.py +0 -0
  39. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud/utils.py +0 -0
  40. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud_git.egg-info/dependency_links.txt +0 -0
  41. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud_git.egg-info/entry_points.txt +0 -0
  42. {mud_git-1.1.6 → mud_git-1.1.8}/src/mud_git.egg-info/top_level.txt +0 -0
@@ -6,11 +6,36 @@ on:
6
6
  workflow_dispatch:
7
7
 
8
8
  jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Set up Python 3.13
15
+ uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.13"
18
+
19
+ - name: Configure git
20
+ run: |
21
+ git config --global user.name "Test User"
22
+ git config --global user.email "test@example.com"
23
+
24
+ - name: Install dependencies and build
25
+ run: |
26
+ pip install -r requirements.txt pytest build
27
+ python -m build
28
+ pip install dist/*.whl
29
+
30
+ - name: Run tests
31
+ run: pytest -v
32
+
9
33
  aur-publish:
34
+ needs: [test]
10
35
  runs-on: ubuntu-latest
11
36
  steps:
12
37
  - name: Checkout repository
13
- uses: actions/checkout@v2
38
+ uses: actions/checkout@v4
14
39
  with:
15
40
  fetch-depth: 0
16
41
 
@@ -0,0 +1,59 @@
1
+ name: Upload Python Package
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Set up Python 3.13
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.13"
21
+
22
+ - name: Configure git
23
+ run: |
24
+ git config --global user.name "Test User"
25
+ git config --global user.email "test@example.com"
26
+
27
+ - name: Install dependencies and build
28
+ run: |
29
+ pip install -r requirements.txt pytest build
30
+ python -m build
31
+ pip install dist/*.whl
32
+
33
+ - name: Run tests
34
+ run: pytest -v
35
+
36
+ deploy:
37
+ needs: [test]
38
+ runs-on: ubuntu-latest
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+
42
+ - name: Set up Python
43
+ uses: actions/setup-python@v5
44
+ with:
45
+ python-version: '3.x'
46
+
47
+ - name: Install dependencies
48
+ run: |
49
+ python -m pip install --upgrade pip
50
+ pip install build
51
+
52
+ - name: Build package
53
+ run: python -m build
54
+
55
+ - name: Publish package
56
+ uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
57
+ with:
58
+ user: __token__
59
+ password: ${{ secrets.TWINE_PAT }}
@@ -0,0 +1,38 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '*'
7
+ pull_request:
8
+ branches:
9
+ - master
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ test:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Set up Python 3.13
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.13"
25
+
26
+ - name: Configure git
27
+ run: |
28
+ git config --global user.name "Test User"
29
+ git config --global user.email "test@example.com"
30
+
31
+ - name: Install dependencies and build
32
+ run: |
33
+ pip install -r requirements.txt pytest build
34
+ python -m build
35
+ pip install dist/*.whl
36
+
37
+ - name: Run tests
38
+ run: pytest -v
@@ -159,3 +159,6 @@ cython_debug/
159
159
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
160
  .idea/
161
161
  .run/
162
+
163
+ # Mac OS
164
+ *.DS_Store
@@ -0,0 +1,78 @@
1
+ # AI Agent Instructions
2
+
3
+ ## What `mud` does
4
+
5
+ `mud` runs git commands (and arbitrary shell commands) across multiple repositories simultaneously. It reads a `.mudconfig` TSV file listing repo paths and optional labels, then dispatches to every matching repo with filtering, async execution, and rich terminal output.
6
+
7
+ For commands, flags, and settings refer to `README.md` — it is the source of truth for user-facing behaviour. For runtime dependencies refer to `requirements.txt`.
8
+
9
+ ## Build and test
10
+
11
+ ```sh
12
+ pip install -e ".[dev]" # install with dev deps
13
+ python -m build # build wheel
14
+ pytest # run tests (verbose by default)
15
+ pytest tests/test_run.py # run a single file
16
+ ```
17
+
18
+ ## Entry point
19
+
20
+ `mud` CLI → `mud:run` in `src/mud/__init__.py` → `App` in `src/mud/app.py`; `mud completion` is dispatched to `completion.complete()` before writable settings are initialised.
21
+
22
+ ## Module map
23
+
24
+ | File | Purpose |
25
+ |---|---|
26
+ | `src/mud/__init__.py` | Entry point; creates `App`, dispatches completion or initialises `Settings` and calls `run()` |
27
+ | `src/mud/app.py` | CLI dispatch; parses args, applies filters, calls `Runner` |
28
+ | `src/mud/completion.py` | Exports the Carapace spec and provides read-only dynamic completion via `C_ARG<n>` / `C_VALUE` |
29
+ | `src/mud/runner.py` | All display commands and execution modes |
30
+ | `src/mud/config.py` | `.mudconfig` TSV read/write; `init`, `add`, `remove`, `prune` |
31
+ | `src/mud/settings.py` | `~/.config/mud/settings.ini` read/write; read-only initialisation for completion |
32
+ | `src/mud/commands.py` | Constants for every command name and filter flag prefix |
33
+ | `src/mud/styles.py` | ANSI escape codes and Nerd Font glyphs |
34
+ | `src/mud/utils.py` | Shared helpers: table creation, error printing, configure wizard |
35
+
36
+ ## Architecture notes
37
+
38
+ **Global settings instance** — `utils.settings` is a single `Settings` object created in `__init__.run()` and accessed across all modules.
39
+
40
+ **Command dispatch** — `App.run()` routes to either a native `Runner` method (matched against constants in `commands.py`) or a shell pass-through. The `--` separator and `-c=<cmd>` flag both reach the shell path.
41
+
42
+ **Carapace completion** — `mud completion carapace` exports a shell-independent spec; its fixed `mud completion values` callback handles mud's equals-only flags without executing user input. Command/flag descriptions come from the argparse definitions, aliases from settings, and label/path/branch values from the discovered config. Local and remote branch names reuse `Runner._get_unique_branch_names()`. Nothing is completed after `--`, inside `-c=`, or in arbitrary shell/alias arguments. Filters remain available after a completed `-c=` token. Neither spec export nor completion creates settings files.
43
+
44
+ **Argument metadata** — argparse registers value-taking options without trailing `=` and toggles as booleans, so empty values and `-t` cannot consume native commands. The execution scanner still requires `-flag=value`; completion inserts the equals sign.
45
+
46
+ **Execution modes** — three modes controlled by `run_async` + `run_table` settings (toggled by `-a` / `-t` flags): sequential, async streamed, async live-table.
47
+
48
+ **Filter chain** — `App._filter_with_arguments()` applies up to nine filters in sequence (ignore label, include/exclude label, include/exclude branch, include/exclude name substring, modified, diverged). Each step removes non-matching repos. Repeated `-N=` / `--not-name=` values exclude any matching path substring; empty exclusions are ignored.
49
+
50
+ **Nerd Fonts** — every glyph in `styles.GLYPHS` has an ASCII fallback. `utils.glyphs(key)` selects between them based on the `nerd_fonts` setting, so mud works with or without a patched font.
51
+
52
+ ## Tests
53
+
54
+ Tests are black-box CLI tests — each runs `python -m mud` as a subprocess against real git repos in a temporary directory.
55
+
56
+ | File | Covers |
57
+ |---|---|
58
+ | `tests/test_config.py` | `init`, `add`, `remove`, `prune` |
59
+ | `tests/test_display.py` | `status`, `info`, `log`, `labels`, `branches`, `tags` |
60
+ | `tests/test_run.py` | Execution modes and flags |
61
+ | `tests/test_filters.py` | `-l=`, `-L=`, `-b=`, `-B=`, `-n=`, `-N=` filter flags and native-command parsing regressions |
62
+ | `tests/test_completion.py` | Completion callback, dynamic values, command boundaries, read-only discovery, and optional real Carapace/Nushell integration |
63
+ | `tests/test_states.py` | Edge-case repo states (unborn, detached, rebasing) |
64
+
65
+ Carapace integration tests run when `carapace` is on `PATH`; the Nushell round-trip test also requires `nu`. Tests isolate `HOME`, Carapace config/cache directories, and use the current Python environment's `mud` executable.
66
+
67
+ ## Knowledge base update rule
68
+ After editing any file under `src/mud/`, update the affected sections of this file before finishing the task.
69
+ `README.md` is the source of truth for user-facing behaviour — if commands, flags, or settings changed, update the relevant table rows there. Table edits only — no new prose.
70
+
71
+ # Git
72
+
73
+ ## Branching
74
+ - All new features should have `feature/` prefix
75
+ - Bugfixes are using `bugfix/` prefix
76
+
77
+ ## Tags
78
+ Tags format is vX.Y.Z
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mud-git
3
- Version: 1.1.6
3
+ Version: 1.1.8
4
4
  Summary: Multi repository git utility. Manage multiple git-repositories simultaneously.
5
5
  Author-email: Jasur Sadikov <jasur@sadikoff.com>
6
6
  License-Expression: MIT
@@ -11,6 +11,8 @@ Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: prettytable
13
13
  Requires-Dist: pygit2
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == "dev"
14
16
  Dynamic: license-file
15
17
 
16
18
  # mud
@@ -32,7 +34,7 @@ pip install mud-git
32
34
  ```
33
35
  **ArchLinux**
34
36
  ```bash
35
- paru -S mud-git
37
+ yay -S mud-git
36
38
  ```
37
39
 
38
40
  For requirements check [requirements.txt](requirements.txt).
@@ -63,18 +65,23 @@ mud --diverged git pull
63
65
  ### Commands
64
66
  | Command | Description |
65
67
  |---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
66
- | `mud set-global` | sets the current `.mudconfig` as a global configuration so it will be used as a fallback configuration to run from any directory. |
68
+ | `mud init` | creates `.mudconfig` and adds repositories beneath the current directory. |
69
+ | `mud configure`/`mud config` | runs the interactive settings wizard. |
70
+ | `mud help`/`mud --help`/`mud -h` | displays available commands and flags. |
71
+ | `mud set-global [path]` | sets the current `.mudconfig`, or the specified configuration path, as the fallback configuration to run from any directory. |
67
72
  | `mud get-config` | prints the current `.mudconfig` location. |
68
73
  | `mud prune` | removes all invalid repositories from the `.mudconfig`. |
69
74
  | `mud info`/`mud i` | displays branch divergence and working directory changes. |
70
75
  | `mud status`/`mud st` | displays working directory changes. |
71
76
  | `mud log`/`mud l` | displays the latest commit message, its time, and its author. |
72
77
  | `mud labels`/`mud lb` | displays mud labels across repositories. |
73
- | `mud branches`/`mud br` | displays all branches in repositories. |
74
- | `mud remote-branches`/`mud rbr` | displays all remote branches in repositories. |
78
+ | `mud branch`/`mud branches`/`mud br` | displays all branches in repositories. |
79
+ | `mud remote-branch`/`mud remote-branches`/`mud rbr` | displays all remote branches in repositories. |
75
80
  | `mud complete-branch` | prints unique current branch names across repositories for shell completion. |
76
81
  | `mud complete-branch-all` | prints unique local and remote branch names across repositories for shell completion. |
77
- | `mud tags`/`mud t` | displays git tags in repositories. |
82
+ | `mud completion carapace` | exports a Carapace spec for all commands, aliases, flags, and native arguments; dynamically completes labels, local/remote branch names, and configured repository paths. |
83
+ | `mud completion values` | internal read-only Carapace callback; reads completion context from `C_ARG<n>` and `C_VALUE`, never executing command text. |
84
+ | `mud tags`/`mud tag`/`mud t` | displays git tags in repositories. |
78
85
 
79
86
  `--` format is also supported. An example would be `mud -- git status`.
80
87
 
@@ -85,11 +92,13 @@ mud --diverged git pull
85
92
  | Flag | Description |
86
93
  |------------------------------------------|--------------------------------------------------------------------------------------|
87
94
  | `-n=<str>` or `--name=<str>` | includes repositories that contains provided string. |
95
+ | `-N=<str>` or `--not-name=<str>` | excludes repositories whose path contains the provided string; repeat to exclude multiple substrings. |
88
96
  | `-l=<label>` or `--label=<label>` | includes repositories with the provided label. |
89
97
  | `-L=<label>` or `--not-label=<label>` | excludes repositories with the provided label. |
90
98
  | `-b=<branch>` or `--branch=<branch>` | includes repositories with the provided branch. |
91
99
  | `-B=<branch>` or `--not-branch=<branch>` | excludes repositories with the provided branch. |
92
- | `-c` or `--command` | explicit command argument. Use this whenever you're trying to run a complex command. |
100
+ | `-c="<command>"` or `--command="<command>"` | explicit shell command; no completion inside its value, but mud filters remain available in following arguments. |
101
+ | `--` | starts an opaque shell command; no mud completion or filtering after this separator. |
93
102
  | `-m` or `--modified` | filters out modified repositories. |
94
103
  | `-d` or `--diverged` | filters repositories with diverged branches. |
95
104
  | `-t` or `--table` | toggles the default table view setting for execution. |
@@ -115,6 +124,18 @@ mud complete-branch
115
124
  mud complete-branch-all
116
125
  ```
117
126
 
127
+ | Carapace Setup | Instructions |
128
+ |----------------|--------------|
129
+ | Requirements | Install `mud` and [Carapace](https://carapace-sh.github.io/carapace-bin/install.html) on `PATH`; the same spec works across Carapace-supported shells. |
130
+ | Install Spec (Nushell, Linux) | `let specs = (($env.XDG_CONFIG_HOME? \| default ($nu.home-path \| path join .config)) \| path join carapace specs)`; `mkdir $specs`; `mud completion carapace \| save --force ($specs \| path join mud.yaml)` |
131
+ | Install Spec (Bash/Zsh, Linux) | `mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/carapace/specs"`; `mud completion carapace > "${XDG_CONFIG_HOME:-$HOME/.config}/carapace/specs/mud.yaml"` |
132
+ | Nushell Hook | Follow [Carapace's Nushell setup](https://carapace-sh.github.io/carapace-bin/setup.html#nushell), or use `{\|spans\| carapace $spans.0 nushell ...$spans \| from json }` as your external completer; preserve empty results for mud rather than falling back to file or command completion. |
133
+ | Bash/Zsh Hook | `source <(carapace _carapace)`; Zsh also requires `autoload -U compinit && compinit`. |
134
+ | Fish Hook | `carapace _carapace fish \| source` |
135
+ | Other Platforms/Shells | Install `mud.yaml` in [Carapace's user spec directory](https://carapace-sh.github.io/carapace-bin/spec/user.html), then follow the appropriate [shell setup](https://carapace-sh.github.io/carapace-bin/setup.html). Restart the shell after first installing the spec. |
136
+ | Dynamic Values | Labels and paths come from the nearest ancestor `.mudconfig` or global fallback; branches include unique local and remote names with remote prefixes removed. Branch filters still match the current branch. No settings or repositories are written during completion. |
137
+ | Command Boundaries | Complete mud filters before a command or `--`, or after a completed `-c="..."` argument. Arbitrary shell commands and alias arguments are not completed. |
138
+
118
139
  ## Settings
119
140
 
120
141
  Settings are stored at `~/.config/mud/settings.ini`.
@@ -148,7 +169,7 @@ You can modify your `.mudconfig` file using the following commands:
148
169
 
149
170
  | Command | Description |
150
171
  |-----------------------------|------------------------------------------------|
151
- | `mud add <path>` | adds a path without a label. |
152
- | `mud add <path> <label>` | adds a path with an optional label. |
153
- | `mud remove <path>` | removes the directory with the specified path. |
154
- | `mud remove <path> <label>` | removes the label from a directory. |
172
+ | `mud add <path>`/`mud a <path>` | adds a path without a label. |
173
+ | `mud add <path> <label>`/`mud a <path> <label>` | adds a path with an optional label. |
174
+ | `mud remove <path>`/`mud rm <path>` | removes the directory with the specified path. |
175
+ | `mud remove <path> <label>`/`mud rm <path> <label>` | removes the label from a directory. |
@@ -17,7 +17,7 @@ pip install mud-git
17
17
  ```
18
18
  **ArchLinux**
19
19
  ```bash
20
- paru -S mud-git
20
+ yay -S mud-git
21
21
  ```
22
22
 
23
23
  For requirements check [requirements.txt](requirements.txt).
@@ -48,18 +48,23 @@ mud --diverged git pull
48
48
  ### Commands
49
49
  | Command | Description |
50
50
  |---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
51
- | `mud set-global` | sets the current `.mudconfig` as a global configuration so it will be used as a fallback configuration to run from any directory. |
51
+ | `mud init` | creates `.mudconfig` and adds repositories beneath the current directory. |
52
+ | `mud configure`/`mud config` | runs the interactive settings wizard. |
53
+ | `mud help`/`mud --help`/`mud -h` | displays available commands and flags. |
54
+ | `mud set-global [path]` | sets the current `.mudconfig`, or the specified configuration path, as the fallback configuration to run from any directory. |
52
55
  | `mud get-config` | prints the current `.mudconfig` location. |
53
56
  | `mud prune` | removes all invalid repositories from the `.mudconfig`. |
54
57
  | `mud info`/`mud i` | displays branch divergence and working directory changes. |
55
58
  | `mud status`/`mud st` | displays working directory changes. |
56
59
  | `mud log`/`mud l` | displays the latest commit message, its time, and its author. |
57
60
  | `mud labels`/`mud lb` | displays mud labels across repositories. |
58
- | `mud branches`/`mud br` | displays all branches in repositories. |
59
- | `mud remote-branches`/`mud rbr` | displays all remote branches in repositories. |
61
+ | `mud branch`/`mud branches`/`mud br` | displays all branches in repositories. |
62
+ | `mud remote-branch`/`mud remote-branches`/`mud rbr` | displays all remote branches in repositories. |
60
63
  | `mud complete-branch` | prints unique current branch names across repositories for shell completion. |
61
64
  | `mud complete-branch-all` | prints unique local and remote branch names across repositories for shell completion. |
62
- | `mud tags`/`mud t` | displays git tags in repositories. |
65
+ | `mud completion carapace` | exports a Carapace spec for all commands, aliases, flags, and native arguments; dynamically completes labels, local/remote branch names, and configured repository paths. |
66
+ | `mud completion values` | internal read-only Carapace callback; reads completion context from `C_ARG<n>` and `C_VALUE`, never executing command text. |
67
+ | `mud tags`/`mud tag`/`mud t` | displays git tags in repositories. |
63
68
 
64
69
  `--` format is also supported. An example would be `mud -- git status`.
65
70
 
@@ -70,11 +75,13 @@ mud --diverged git pull
70
75
  | Flag | Description |
71
76
  |------------------------------------------|--------------------------------------------------------------------------------------|
72
77
  | `-n=<str>` or `--name=<str>` | includes repositories that contains provided string. |
78
+ | `-N=<str>` or `--not-name=<str>` | excludes repositories whose path contains the provided string; repeat to exclude multiple substrings. |
73
79
  | `-l=<label>` or `--label=<label>` | includes repositories with the provided label. |
74
80
  | `-L=<label>` or `--not-label=<label>` | excludes repositories with the provided label. |
75
81
  | `-b=<branch>` or `--branch=<branch>` | includes repositories with the provided branch. |
76
82
  | `-B=<branch>` or `--not-branch=<branch>` | excludes repositories with the provided branch. |
77
- | `-c` or `--command` | explicit command argument. Use this whenever you're trying to run a complex command. |
83
+ | `-c="<command>"` or `--command="<command>"` | explicit shell command; no completion inside its value, but mud filters remain available in following arguments. |
84
+ | `--` | starts an opaque shell command; no mud completion or filtering after this separator. |
78
85
  | `-m` or `--modified` | filters out modified repositories. |
79
86
  | `-d` or `--diverged` | filters repositories with diverged branches. |
80
87
  | `-t` or `--table` | toggles the default table view setting for execution. |
@@ -100,6 +107,18 @@ mud complete-branch
100
107
  mud complete-branch-all
101
108
  ```
102
109
 
110
+ | Carapace Setup | Instructions |
111
+ |----------------|--------------|
112
+ | Requirements | Install `mud` and [Carapace](https://carapace-sh.github.io/carapace-bin/install.html) on `PATH`; the same spec works across Carapace-supported shells. |
113
+ | Install Spec (Nushell, Linux) | `let specs = (($env.XDG_CONFIG_HOME? \| default ($nu.home-path \| path join .config)) \| path join carapace specs)`; `mkdir $specs`; `mud completion carapace \| save --force ($specs \| path join mud.yaml)` |
114
+ | Install Spec (Bash/Zsh, Linux) | `mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/carapace/specs"`; `mud completion carapace > "${XDG_CONFIG_HOME:-$HOME/.config}/carapace/specs/mud.yaml"` |
115
+ | Nushell Hook | Follow [Carapace's Nushell setup](https://carapace-sh.github.io/carapace-bin/setup.html#nushell), or use `{\|spans\| carapace $spans.0 nushell ...$spans \| from json }` as your external completer; preserve empty results for mud rather than falling back to file or command completion. |
116
+ | Bash/Zsh Hook | `source <(carapace _carapace)`; Zsh also requires `autoload -U compinit && compinit`. |
117
+ | Fish Hook | `carapace _carapace fish \| source` |
118
+ | Other Platforms/Shells | Install `mud.yaml` in [Carapace's user spec directory](https://carapace-sh.github.io/carapace-bin/spec/user.html), then follow the appropriate [shell setup](https://carapace-sh.github.io/carapace-bin/setup.html). Restart the shell after first installing the spec. |
119
+ | Dynamic Values | Labels and paths come from the nearest ancestor `.mudconfig` or global fallback; branches include unique local and remote names with remote prefixes removed. Branch filters still match the current branch. No settings or repositories are written during completion. |
120
+ | Command Boundaries | Complete mud filters before a command or `--`, or after a completed `-c="..."` argument. Arbitrary shell commands and alias arguments are not completed. |
121
+
103
122
  ## Settings
104
123
 
105
124
  Settings are stored at `~/.config/mud/settings.ini`.
@@ -133,7 +152,7 @@ You can modify your `.mudconfig` file using the following commands:
133
152
 
134
153
  | Command | Description |
135
154
  |-----------------------------|------------------------------------------------|
136
- | `mud add <path>` | adds a path without a label. |
137
- | `mud add <path> <label>` | adds a path with an optional label. |
138
- | `mud remove <path>` | removes the directory with the specified path. |
139
- | `mud remove <path> <label>` | removes the label from a directory. |
155
+ | `mud add <path>`/`mud a <path>` | adds a path without a label. |
156
+ | `mud add <path> <label>`/`mud a <path> <label>` | adds a path with an optional label. |
157
+ | `mud remove <path>`/`mud rm <path>` | removes the directory with the specified path. |
158
+ | `mud remove <path> <label>`/`mud rm <path> <label>` | removes the label from a directory. |
@@ -22,6 +22,14 @@ local_scheme = "no-local-version"
22
22
  [tool.hatch.build.targets.wheel]
23
23
  packages = ["src/mud"]
24
24
 
25
+ [project.optional-dependencies]
26
+ dev = ["pytest"]
27
+
28
+ [tool.pytest.ini_options]
29
+ testpaths = ["tests"]
30
+ pythonpath = ["tests"]
31
+ addopts = "-v"
32
+
25
33
  [project.scripts]
26
34
  mud = "mud:run"
27
35
 
@@ -2,15 +2,23 @@
2
2
 
3
3
  import mud.utils
4
4
  import mud.settings
5
+ import sys
5
6
 
6
7
  from mud.app import App
8
+ from mud.commands import COMPLETION
7
9
 
8
10
 
9
11
  def run():
10
12
  try:
13
+ app: App = App()
14
+ if sys.argv[1:2] == COMPLETION:
15
+ from mud.completion import complete
16
+
17
+ complete(app.parser)
18
+ return
19
+
11
20
  utils.settings = settings.Settings(utils.SETTINGS_FILE_NAME, utils.OLD_SETTINGS_FILE_NAME)
12
21
 
13
- app: App = App()
14
22
  app.run()
15
23
  except KeyboardInterrupt:
16
24
  utils.print_error(0)
@@ -35,7 +35,10 @@ class App:
35
35
 
36
36
  @staticmethod
37
37
  def _create_parser() -> ArgumentParser:
38
- parser = ArgumentParser(description=f'mud allows you to run commands in multiple repositories.')
38
+ parser = ArgumentParser(
39
+ description=f'mud allows you to run commands in multiple repositories.',
40
+ usage='%(prog)s [OPTIONS] [COMMAND] ...',
41
+ )
39
42
  subparsers = parser.add_subparsers(dest='command')
40
43
 
41
44
  subparsers.add_parser(LOG[0], aliases=LOG[1:], help='Displays log of latest commit messages for all repositories in a table view.')
@@ -48,6 +51,8 @@ class App:
48
51
  subparsers.add_parser(REMOTE_BRANCHES[0], aliases=REMOTE_BRANCHES[1:], help='Displays all remote branches in repositories.')
49
52
  subparsers.add_parser(COMPLETE_BRANCH[0], help='Prints unique current branch names across repositories for shell completion.')
50
53
  subparsers.add_parser(COMPLETE_BRANCH_ALL[0], help='Prints unique local and remote branch names across repositories for shell completion.')
54
+ completion_parser = subparsers.add_parser(COMPLETION[0], help='Prints shell completion support.')
55
+ completion_parser.add_argument('shell', choices=['carapace', 'values'], help='Completion format (values is the Carapace callback).')
51
56
  subparsers.add_parser(CONFIGURE[0], aliases=CONFIGURE[1:], help='Runs the interactive configuration wizard.')
52
57
  subparsers.add_parser(GET_CONFIG[0], aliases=GET_CONFIG[1:], help='Prints current .mudconfig path.')
53
58
  subparsers.add_parser(SET_GLOBAL[0], aliases=SET_GLOBAL[1:], help='Sets .mudconfig in the current repository as your fallback .mudconfig.')
@@ -62,13 +67,17 @@ class App:
62
67
 
63
68
  subparsers.add_parser(PRUNE[0], help='Removes invalid paths from .mudconfig.')
64
69
 
65
- parser.add_argument(*COMMAND_ATTR, metavar='COMMAND', help=f'Explicit command argument. Use this when you want to run a command that has a special characters.', nargs='?', default='', type=str)
66
- parser.add_argument(*TABLE_ATTR, metavar='TABLE', help=f'Switches table view, runs in table view it is disabled in .mudsettings.', nargs='?', default='', type=str)
67
- parser.add_argument(*NAME_PREFIX, metavar='NAME', help='Includes repositories where name contains provided value.', nargs='?', default='', type=str)
68
- parser.add_argument(*LABEL_PREFIX, metavar='LABEL', help='Includes repositories with provided label.', nargs='?', default='', type=str)
69
- parser.add_argument(*NOT_LABEL_PREFIX, metavar='NOT_LABEL', help=f'Excludes repositories with provided label.', nargs='?', default='', type=str)
70
- parser.add_argument(*BRANCH_PREFIX, metavar='BRANCH', help='Includes repositories on a provided branch.', nargs='?', default='', type=str)
71
- parser.add_argument(*NOT_BRANCH_PREFIX, metavar='NOT_BRANCH', help='Excludes repositories on a provided branch.', nargs='?', default='', type=str)
70
+ for prefixes, metavar, description in [
71
+ (COMMAND_ATTR, 'COMMAND', 'Explicit shell command; use for complex commands.'),
72
+ (NAME_PREFIX, 'NAME', 'Includes repositories where name contains provided value.'),
73
+ (NOT_NAME_PREFIX, 'NOT_NAME', 'Excludes repositories where name contains provided value.'),
74
+ (LABEL_PREFIX, 'LABEL', 'Includes repositories with provided label.'),
75
+ (NOT_LABEL_PREFIX, 'NOT_LABEL', 'Excludes repositories with provided label.'),
76
+ (BRANCH_PREFIX, 'BRANCH', 'Includes repositories on a provided branch.'),
77
+ (NOT_BRANCH_PREFIX, 'NOT_BRANCH', 'Excludes repositories on a provided branch.'),
78
+ ]:
79
+ parser.add_argument(*(prefix.rstrip('=') for prefix in prefixes), metavar=metavar, help=description, default='', type=str)
80
+ parser.add_argument(*TABLE_ATTR, action='store_true', help='Toggles the table view setting for execution.')
72
81
  parser.add_argument(*MODIFIED_ATTR, action='store_true', help='Filters modified repositories.')
73
82
  parser.add_argument(*DIVERGED_ATTR, action='store_true', help='Filters repositories with diverged branches.')
74
83
  parser.add_argument(*ASYNC_ATTR, action='store_true', help='Switches asynchronous run feature.')
@@ -130,7 +139,7 @@ class App:
130
139
 
131
140
  # Handling commands
132
141
  if native_command:
133
- args = self.parser.parse_args()
142
+ args, _ = self.parser.parse_known_args()
134
143
 
135
144
  if args.command in INIT + ADD + REMOVE + PRUNE + GET_CONFIG:
136
145
  if args.command in GET_CONFIG:
@@ -226,6 +235,7 @@ class App:
226
235
  include_labels = []
227
236
  exclude_labels = []
228
237
  contains_strings = []
238
+ not_contains_strings = []
229
239
  include_branches = []
230
240
  exclude_branches = []
231
241
  modified = False
@@ -250,6 +260,10 @@ class App:
250
260
  exclude_branches.append(arg.split('=', 1)[1])
251
261
  elif any(arg.startswith(prefix) for prefix in NAME_PREFIX):
252
262
  contains_strings.append(arg.split('=', 1)[1])
263
+ elif any(arg.startswith(prefix) for prefix in NOT_NAME_PREFIX):
264
+ value = arg.split('=', 1)[1]
265
+ if value:
266
+ not_contains_strings.append(value)
253
267
  elif arg in MODIFIED_ATTR:
254
268
  modified = True
255
269
  elif arg in DIVERGED_ATTR:
@@ -294,6 +308,8 @@ class App:
294
308
  delete = True
295
309
  if any(contains_strings) and not any(substr in path for substr in contains_strings):
296
310
  delete = True
311
+ if any(not_contains_strings) and any(substr in path for substr in not_contains_strings):
312
+ delete = True
297
313
 
298
314
  if not delete and not repo.head_is_unborn and (any(include_branches) or any(exclude_branches)):
299
315
  if any(include_branches) and repo.head.shorthand not in include_branches:
@@ -12,12 +12,13 @@ BRANCHES = ['branch', 'branches', 'br']
12
12
  REMOTE_BRANCHES = ['remote-branch', 'remote-branches', 'rbr']
13
13
  COMPLETE_BRANCH = ['complete-branch']
14
14
  COMPLETE_BRANCH_ALL = ['complete-branch-all']
15
+ COMPLETION = ['completion']
15
16
  HELP = ['help', '--help', '-h']
16
17
  CONFIGURE = ['configure', 'config']
17
18
  GET_CONFIG = ['get-config']
18
19
  SET_GLOBAL = ['set-global']
19
20
 
20
- COMMANDS = ADD + REMOVE + PRUNE + LOG + INFO + INIT + TAGS + LABELS + STATUS + BRANCHES + REMOTE_BRANCHES + COMPLETE_BRANCH + COMPLETE_BRANCH_ALL + HELP + CONFIGURE + SET_GLOBAL + GET_CONFIG
21
+ COMMANDS = ADD + REMOVE + PRUNE + LOG + INFO + INIT + TAGS + LABELS + STATUS + BRANCHES + REMOTE_BRANCHES + COMPLETE_BRANCH + COMPLETE_BRANCH_ALL + COMPLETION + HELP + CONFIGURE + SET_GLOBAL + GET_CONFIG
21
22
 
22
23
  # Filters
23
24
  ASYNC_ATTR = '-a', '--async'
@@ -25,6 +26,7 @@ TABLE_ATTR = '-t', '--table'
25
26
  MODIFIED_ATTR = '-m', '--modified'
26
27
  DIVERGED_ATTR = '-d', '--diverged'
27
28
  NAME_PREFIX = '-n=', '--name='
29
+ NOT_NAME_PREFIX = '-N=', '--not-name='
28
30
  LABEL_PREFIX = '-l=', '--label='
29
31
  COMMAND_ATTR = '-c=', '--command='
30
32
  BRANCH_PREFIX = '-b=', '--branch='