gitstow 0.2.8__tar.gz → 0.3.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.
Files changed (109) hide show
  1. {gitstow-0.2.8 → gitstow-0.3.0}/.github/workflows/ci.yml +5 -1
  2. {gitstow-0.2.8 → gitstow-0.3.0}/BACKLOG.md +2 -2
  3. {gitstow-0.2.8 → gitstow-0.3.0}/CHANGELOG.md +31 -0
  4. {gitstow-0.2.8 → gitstow-0.3.0}/CLAUDE.md +19 -18
  5. {gitstow-0.2.8 → gitstow-0.3.0}/PKG-INFO +20 -18
  6. {gitstow-0.2.8 → gitstow-0.3.0}/README.md +8 -10
  7. {gitstow-0.2.8 → gitstow-0.3.0}/docs/building/audit-2026-07-06.md +22 -22
  8. {gitstow-0.2.8 → gitstow-0.3.0}/docs/user/commands.md +38 -31
  9. {gitstow-0.2.8 → gitstow-0.3.0}/docs/user/getting-started.md +5 -1
  10. {gitstow-0.2.8 → gitstow-0.3.0}/pyproject.toml +13 -9
  11. {gitstow-0.2.8 → gitstow-0.3.0}/scripts/release.sh +6 -0
  12. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/__init__.py +1 -1
  13. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/add.py +100 -34
  14. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/config_cmd.py +6 -4
  15. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/doctor.py +17 -0
  16. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/exec_cmd.py +4 -1
  17. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/export_cmd.py +75 -21
  18. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/fetch.py +31 -68
  19. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/helpers.py +22 -0
  20. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/list_cmd.py +4 -1
  21. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/main.py +0 -2
  22. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/manage.py +4 -1
  23. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/onboard.py +2 -1
  24. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/open_cmd.py +25 -11
  25. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/pull.py +46 -74
  26. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/search.py +43 -30
  27. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/serve.py +3 -2
  28. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/status.py +43 -30
  29. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/workspace_cmd.py +9 -0
  30. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/config.py +3 -0
  31. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/discovery.py +11 -7
  32. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/git.py +22 -5
  33. gitstow-0.3.0/src/gitstow/core/operations.py +98 -0
  34. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/repo.py +32 -1
  35. gitstow-0.3.0/src/gitstow/core/status_model.py +119 -0
  36. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/mcp/server.py +85 -43
  37. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/skill/SKILL.md +5 -3
  38. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/routes/collection.py +5 -1
  39. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/routes/dashboard.py +57 -28
  40. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/routes/pages.py +10 -1
  41. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/routes/repos.py +53 -26
  42. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/_repo_drawer.html +1 -9
  43. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/dashboard.html +7 -7
  44. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/partials/pull_summary.html +11 -0
  45. gitstow-0.3.0/tests/test_cli.py +885 -0
  46. {gitstow-0.2.8 → gitstow-0.3.0}/tests/test_config.py +9 -0
  47. {gitstow-0.2.8 → gitstow-0.3.0}/tests/test_git.py +22 -0
  48. gitstow-0.3.0/tests/test_mcp.py +138 -0
  49. gitstow-0.3.0/tests/test_operations.py +67 -0
  50. {gitstow-0.2.8 → gitstow-0.3.0}/tests/test_repo.py +51 -0
  51. {gitstow-0.2.8 → gitstow-0.3.0}/tests/test_serve.py +162 -0
  52. gitstow-0.3.0/tests/test_status_model.py +83 -0
  53. gitstow-0.2.8/src/gitstow/cli/tui.py +0 -27
  54. gitstow-0.2.8/src/gitstow/tui/__init__.py +0 -1
  55. gitstow-0.2.8/src/gitstow/tui/app.py +0 -384
  56. gitstow-0.2.8/tests/test_cli.py +0 -266
  57. {gitstow-0.2.8 → gitstow-0.3.0}/.claude/settings.json +0 -0
  58. {gitstow-0.2.8 → gitstow-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  59. {gitstow-0.2.8 → gitstow-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  60. {gitstow-0.2.8 → gitstow-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  61. {gitstow-0.2.8 → gitstow-0.3.0}/.github/workflows/publish.yml +0 -0
  62. {gitstow-0.2.8 → gitstow-0.3.0}/.gitignore +0 -0
  63. {gitstow-0.2.8 → gitstow-0.3.0}/AGENTS.md +0 -0
  64. {gitstow-0.2.8 → gitstow-0.3.0}/CODE_OF_CONDUCT.md +0 -0
  65. {gitstow-0.2.8 → gitstow-0.3.0}/CONTRIBUTING.md +0 -0
  66. {gitstow-0.2.8 → gitstow-0.3.0}/LICENSE +0 -0
  67. {gitstow-0.2.8 → gitstow-0.3.0}/SECURITY.md +0 -0
  68. {gitstow-0.2.8 → gitstow-0.3.0}/demo.gif +0 -0
  69. {gitstow-0.2.8 → gitstow-0.3.0}/demo.tape +0 -0
  70. {gitstow-0.2.8 → gitstow-0.3.0}/docs/building/implementation-plan.md +0 -0
  71. {gitstow-0.2.8 → gitstow-0.3.0}/docs/building/plans/2026-07-06-wave-1-correctness-safety.md +0 -0
  72. {gitstow-0.2.8 → gitstow-0.3.0}/docs/building/plans/2026-07-06-wave-2-status-model.md +0 -0
  73. {gitstow-0.2.8 → gitstow-0.3.0}/docs/building/plans/2026-07-06-wave-3-structure-polish.md +0 -0
  74. {gitstow-0.2.8 → gitstow-0.3.0}/docs/user/concepts.md +0 -0
  75. {gitstow-0.2.8 → gitstow-0.3.0}/docs/user/configuration.md +0 -0
  76. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/__main__.py +0 -0
  77. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/__init__.py +0 -0
  78. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/migrate.py +0 -0
  79. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/remove.py +0 -0
  80. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/setup_ai.py +0 -0
  81. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/shell.py +0 -0
  82. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/skill_cmd.py +0 -0
  83. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/stats.py +0 -0
  84. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/cli/update.py +0 -0
  85. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/__init__.py +0 -0
  86. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/locking.py +0 -0
  87. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/parallel.py +0 -0
  88. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/paths.py +0 -0
  89. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/core/url_parser.py +0 -0
  90. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/mcp/__init__.py +0 -0
  91. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/__init__.py +0 -0
  92. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/routes/__init__.py +0 -0
  93. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/routes/system.py +0 -0
  94. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/routes/workspaces.py +0 -0
  95. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/server.py +0 -0
  96. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/static/app.css +0 -0
  97. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/add_repo.html +0 -0
  98. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/base.html +0 -0
  99. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/partials/dashboard_rows.html +0 -0
  100. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/partials/fetch_summary.html +0 -0
  101. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/partials/repo_row.html +0 -0
  102. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/settings.html +0 -0
  103. {gitstow-0.2.8 → gitstow-0.3.0}/src/gitstow/web/templates/workspaces.html +0 -0
  104. {gitstow-0.2.8 → gitstow-0.3.0}/tests/__init__.py +0 -0
  105. {gitstow-0.2.8 → gitstow-0.3.0}/tests/conftest.py +0 -0
  106. {gitstow-0.2.8 → gitstow-0.3.0}/tests/test_locking.py +0 -0
  107. {gitstow-0.2.8 → gitstow-0.3.0}/tests/test_onboard.py +0 -0
  108. {gitstow-0.2.8 → gitstow-0.3.0}/tests/test_setup_ai.py +0 -0
  109. {gitstow-0.2.8 → gitstow-0.3.0}/tests/test_url_parser.py +0 -0
@@ -8,10 +8,14 @@ on:
8
8
 
9
9
  jobs:
10
10
  test:
11
- runs-on: ubuntu-latest
12
11
  strategy:
13
12
  matrix:
13
+ os: [ubuntu-latest]
14
14
  python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+ include:
16
+ - os: macos-latest
17
+ python-version: "3.12"
18
+ runs-on: ${{ matrix.os }}
15
19
 
16
20
  steps:
17
21
  - uses: actions/checkout@v4
@@ -9,9 +9,9 @@ Post-v0.1.0 improvements tracked here. See [GitHub Issues](https://github.com/ri
9
9
  - [x] **Network retry/resume for batch operations** (#3) — `--retry N` flag on add and pull; cleans up partial clones before retry
10
10
  - [x] **Export format versioning and checksums** (#4) — Added `version: 1` to YAML/JSON exports; validates on import; backward-compatible with unversioned files
11
11
  - [x] **Progress indication during long clones** (#5) — `git clone --progress`; pull shows live counter `[5/47]`
12
- - [x] **Publish to PyPI** (#6) — Release workflow created (`.github/workflows/release.yml`); publish on GitHub release
12
+ - [x] **Publish to PyPI** (#6) — Release workflow created (`.github/workflows/publish.yml`); publish on GitHub release
13
13
  - [x] **Web dashboard (`gitstow ui`)** — Dark local browser UI wrapping core/ for daily repo management. FastAPI + Jinja2 + HTMX, dark + ember accent aesthetic. See [CHANGELOG.md](CHANGELOG.md) v0.2.0.
14
- - [ ] **Fix TUI breakage** — Multiple issues across the Textual TUI; currently unusable in daily flow. Keep around for SSH/remote use case but triage and repair when time allows.
14
+ - [x] **Fix TUI breakage** — Resolved by retirement: the Textual TUI was removed in v0.3.0; `gitstow ui` (web dashboard) is the visual surface going forward.
15
15
 
16
16
  ## Documentation
17
17
 
@@ -4,6 +4,37 @@ All notable changes to gitstow will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.3.0] - 2026-07-12
8
+
9
+ ### Added
10
+
11
+ - **Composition-aware status everywhere.** `gitstow status` now shows a Local Changes column (modified/staged/untracked counts) and a separate Remote column (in-sync/ahead/behind/diverged). The web dashboard rows, repo drawer, and help legend all use the same shared classifier — a repo with only staged or untracked files can no longer display as "clean". JSON output gains additive `local` and `remote` keys (all existing keys preserved).
12
+ - **`gitstow fetch_repos` MCP tool** and a `last_fetched` timestamp in all JSON outputs — MCP clients can now see fetch state, matching the CLI and web dashboard.
13
+ - **Untracked-repo hints on `list` and `status`.** Repos found on disk but not in the registry are now surfaced with a hint pointing at `workspace scan`, instead of being silently invisible.
14
+ - **`doctor` detects orphaned workspaces**, and `workspace remove` now warns when repos would be left pointing at a removed workspace.
15
+ - **`clone_timeout` setting** (`gitstow config set clone_timeout <seconds>`) — clone operations on very large repos no longer fail on a hardcoded 300s limit.
16
+ - **macOS added to the CI test matrix**, matching the packaging classifiers.
17
+ - **CHANGELOG gate in the release script** — `scripts/release.sh` now refuses to cut a release without a corresponding changelog entry.
18
+
19
+ ### Changed
20
+
21
+ - **`add` clones multiple repos in parallel** and now detects remote mismatches and duplicate URLs before cloning, instead of silently registering whatever is already on disk.
22
+ - **`search` now runs across repos in parallel**, matching `exec`.
23
+ - **Pull, fetch, and the MCP server share one operations layer.** The MCP server now follows the same bulk-pull rule as the CLI and web dashboard (modified/staged skip, diverged skip, untracked-only pulls).
24
+ - **`open` prefers `$VISUAL`/`$EDITOR`** over guessing an editor, and runs terminal editors in the foreground so they actually get a usable TTY.
25
+ - **Disk sizing (`stats`, `repo info`) now shells out to `du`** instead of walking every file in Python — much faster on large repos.
26
+ - **`collection import` honors each entry's recorded workspace** instead of dumping everything into one workspace, and now fails loudly (with a printed error) on newer file versions instead of exiting silently.
27
+ - **The web dashboard's dependencies (FastAPI, uvicorn, Jinja2) moved to the optional `[ui]` extra.** Existing installs keep working; fresh CLI-only installs are ~15 packages lighter. Install with `pip install "gitstow[ui]"` to keep the dashboard.
28
+ - **Bulk pull rule unified and refined (behavior change).** CLI `pull` and the dashboard's Pull-all now follow one rule: repos with modified or staged files are skipped (with the composition shown), diverged repos are skipped (fast-forward pull cannot succeed), and repos with only untracked files ARE pulled — previously the CLI skipped them forever while the web pulled everything.
29
+
30
+ ### Fixed
31
+
32
+ - **`search`, `exec`, and `status` `--json` now emit pure JSON on an empty filter match** — previously they printed a "no repos match" banner and no payload at all, even with `--json` set.
33
+
34
+ ### Removed
35
+
36
+ - **The Textual TUI (`gitstow tui`).** The web dashboard (`gitstow ui`) replaced it as the visual surface in v0.2.0; the TUI had been broken and parked since. Git history preserves it.
37
+
7
38
  ## [0.2.8] - 2026-07-12
8
39
 
9
40
  ### Fixed
@@ -25,21 +25,20 @@ src/gitstow/
25
25
  │ ├── exec_cmd.py, search.py, open_cmd.py, stats.py # Power commands
26
26
  │ ├── export_cmd.py # export/import collection
27
27
  │ ├── shell.py # Shell integration (fzf, aliases)
28
- │ ├── tui.py # TUI launcher
29
28
  │ ├── serve.py # Web dashboard launcher
30
29
  │ ├── migrate.py, config_cmd.py, onboard.py, doctor.py, skill_cmd.py
31
30
  │ └── __init__.py
32
31
  ├── core/ # Business logic (git ops, URL parsing, state)
33
- │ ├── paths.py # Path constants, central repos.yaml resolution
34
- │ ├── config.py # Workspace + Settings dataclasses, load/save
35
- │ ├── repo.py # Repo dataclass + RepoStore (nested YAML CRUD)
36
- │ ├── url_parser.py # URL → (host, owner, repo) extraction
37
- │ ├── git.py # All git subprocess calls
38
- │ ├── discovery.py # Walk directory tree (structured + flat), reconcile
39
- │ ├── parallel.py # Async execution with semaphore
40
- └── __init__.py
41
- ├── tui/ # Textual interactive dashboard
42
- │ ├── app.py # Main TUI application
32
+ │ ├── paths.py # Path constants, central repos.yaml resolution
33
+ │ ├── config.py # Workspace + Settings dataclasses, load/save
34
+ │ ├── repo.py # Repo dataclass + RepoStore (nested YAML CRUD)
35
+ │ ├── url_parser.py # URL → (host, owner, repo) extraction
36
+ │ ├── git.py # All git subprocess calls
37
+ │ ├── discovery.py # Walk directory tree (structured + flat), reconcile
38
+ │ ├── parallel.py # Async execution with semaphore
39
+ ├── status_model.py # Shared repo-state classifier — local composition vs remote relationship
40
+ ├── operations.py # Shared filter + bulk-runner layer (pull/fetch/MCP)
41
+ │ ├── locking.py # Cross-process file lock guarding repos.yaml writes
43
42
  │ └── __init__.py
44
43
  ├── web/ # FastAPI browser dashboard (gitstow ui)
45
44
  │ ├── server.py # FastAPI app, uvicorn runner, app.state.server stash
@@ -64,15 +63,18 @@ src/gitstow/
64
63
  - `core/url_parser.py` — URL parsing (the hardest part). Test changes here thoroughly.
65
64
  - `core/git.py` — All git subprocess calls. Uses `git status --porcelain=v2 --branch` for single-call efficiency.
66
65
  - `core/parallel.py` — Async execution with semaphore (max 6 concurrent).
66
+ - `core/status_model.py` — `RepoState` — the single source of truth for local (modified/staged/untracked) vs remote (in-sync/ahead/behind/diverged) classification, consumed by CLI, web, and JSON.
67
+ - `core/operations.py` — Shared `filter_repo_pairs()` + bulk-runner used by `pull`, `fetch`, and the MCP server so surfaces can't drift.
68
+ - `core/locking.py` — `file_lock()` cross-process advisory lock guarding `repos.yaml` against concurrent CLI/web writes.
67
69
  - `cli/helpers.py` — Shared workspace resolution used by all CLI commands.
68
70
  - `cli/workspace_cmd.py` — workspace list/add/remove/scan subcommands.
69
71
  - `cli/main.py` — Typer app, global `-w/--workspace` option, command registration.
70
- - `tui/app.py` — Textual dashboard with DataTable, filter, pull, freeze toggle.
71
72
 
72
73
  ## Data Files
73
74
 
74
75
  - `~/.gitstow/config.yaml` — Settings (workspaces list, default host, SSH pref).
75
76
  - `~/.gitstow/repos.yaml` — Repo metadata nested by workspace label. Central location.
77
+ - `~/.gitstow/repos.lock` — Cross-process advisory lock file (`core/locking.py`) held during `repos.yaml` writes. Not user-facing data; safe to ignore/delete if orphaned.
76
78
 
77
79
  ### repos.yaml format
78
80
  ```yaml
@@ -86,14 +88,14 @@ active:
86
88
  tags: [active]
87
89
  ```
88
90
 
89
- ## All Commands (32)
91
+ ## All Commands (37)
90
92
 
91
93
  **Core:** `add`, `pull`, `fetch`, `list`, `status`, `remove`, `migrate`
92
94
  **Workspace:** `workspace list`, `workspace add`, `workspace remove`, `workspace scan`
93
95
  **Repo management:** `repo freeze`, `repo unfreeze`, `repo tag`, `repo untag`, `repo tags`, `repo info`
94
96
  **Power:** `exec`, `search`, `open`, `stats`
95
97
  **Sharing:** `collection export`, `collection import`
96
- **Shell:** `shell pick`, `shell init`, `shell completions`, `shell setup`, `tui`, `ui`
98
+ **Shell:** `shell pick`, `shell init`, `shell completions`, `shell setup`, `ui`
97
99
  **Setup:** `onboard`, `config show/set/path/migrate-root`, `doctor`, `install-skill`, `setup-ai`, `update`
98
100
 
99
101
  ## Development
@@ -101,9 +103,8 @@ active:
101
103
  ```bash
102
104
  cd ~/labs/projects/gitstow
103
105
  pip install -e ".[dev]"
104
- pytest # 44 tests
106
+ pytest # full suite — keep green
105
107
  ruff check src/
106
- pip install -e ".[tui]" # For TUI development
107
108
  ```
108
109
 
109
110
  ## Patterns
@@ -123,7 +124,7 @@ pip install -e ".[tui]" # For TUI development
123
124
 
124
125
  - Prefer proper long-term solutions over shortcut patches. If a feature is incomplete in one surface, build the feature into that surface instead of papering over it with wording, partial conditionals, or one-off display logic.
125
126
  - Do not recommend quick fixes, temporary patches, or narrow workarounds unless the user explicitly asks for a shortcut.
126
- - Keep CLI, TUI, web dashboard, JSON output, docs, and tests semantically aligned when changing user-facing status behavior.
127
+ - Keep CLI, web dashboard, JSON output, docs, and tests semantically aligned when changing user-facing status behavior.
127
128
  - For repo state presentation, avoid using "dirty" as a broad user-facing bucket for every local change. Present it as local/uncommitted changes with the composition visible: modified, staged, and untracked counts.
128
129
  - Keep local working-tree state separate from remote relationship state. For example: local changes, clean, ahead, behind, diverged, frozen, missing.
129
130
  - When improving the web dashboard, implement the actual missing dashboard feature and shared classification/model behavior instead of copying a CLI-only assumption into the template.
@@ -139,6 +140,6 @@ pip install -e ".[tui]" # For TUI development
139
140
  **Optional: MCP server** (`src/gitstow/mcp/server.py`)
140
141
  - For non-Claude-Code AI tools (Claude Desktop, Cursor, Windsurf)
141
142
  - Install: `pip install gitstow[mcp]`, entry point: `gitstow-mcp`
142
- - 13 tools + 3 resources, wraps same `core/` modules as CLI
143
+ - 14 tools + 3 resources, wraps same `core/` modules as CLI
143
144
  - **Tradeoff:** MCP tools are always loaded into context (costs tokens even when idle).
144
145
  The skill has no such cost. Only use MCP for dedicated repo-management setups.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitstow
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: A git repository library manager — clone, organize, and maintain collections of repos you learn from
5
5
  Project-URL: Homepage, https://github.com/rishmadaan/gitstow
6
6
  Project-URL: Repository, https://github.com/rishmadaan/gitstow
@@ -26,21 +26,25 @@ Classifier: Programming Language :: Python :: 3.13
26
26
  Classifier: Topic :: Software Development :: Version Control :: Git
27
27
  Requires-Python: >=3.10
28
28
  Requires-Dist: beaupy>=3.0
29
- Requires-Dist: fastapi>=0.110
30
- Requires-Dist: jinja2>=3.1
31
- Requires-Dist: python-multipart>=0.0.9
32
29
  Requires-Dist: pyyaml>=6.0
33
30
  Requires-Dist: rich>=13.0
34
- Requires-Dist: typer[all]>=0.9
35
- Requires-Dist: uvicorn>=0.27
31
+ Requires-Dist: typer>=0.9
36
32
  Provides-Extra: dev
33
+ Requires-Dist: fastapi>=0.110; extra == 'dev'
37
34
  Requires-Dist: httpx>=0.27; extra == 'dev'
35
+ Requires-Dist: jinja2>=3.1; extra == 'dev'
36
+ Requires-Dist: mcp>=1.0; extra == 'dev'
38
37
  Requires-Dist: pytest>=8.0; extra == 'dev'
38
+ Requires-Dist: python-multipart>=0.0.9; extra == 'dev'
39
39
  Requires-Dist: ruff>=0.4; extra == 'dev'
40
+ Requires-Dist: uvicorn>=0.27; extra == 'dev'
40
41
  Provides-Extra: mcp
41
42
  Requires-Dist: mcp>=1.0; extra == 'mcp'
42
- Provides-Extra: tui
43
- Requires-Dist: textual>=0.50; extra == 'tui'
43
+ Provides-Extra: ui
44
+ Requires-Dist: fastapi>=0.110; extra == 'ui'
45
+ Requires-Dist: jinja2>=3.1; extra == 'ui'
46
+ Requires-Dist: python-multipart>=0.0.9; extra == 'ui'
47
+ Requires-Dist: uvicorn>=0.27; extra == 'ui'
44
48
  Description-Content-Type: text/markdown
45
49
 
46
50
  # gitstow
@@ -62,8 +66,8 @@ Existing tools solve parts of this:
62
66
  ## Quick Start
63
67
 
64
68
  ```bash
65
- pipx install gitstow # recommended
66
- # or: pip install gitstow
69
+ pipx install "gitstow[ui]" # recommended — CLI + browser dashboard
70
+ # or: pip install gitstow # CLI only; add [ui] for the dashboard
67
71
 
68
72
  # First-run setup (optional — works without it)
69
73
  gitstow onboard
@@ -99,6 +103,8 @@ gitstow workspace scan active
99
103
  ```bash
100
104
  gitstow add anthropic/claude-code facebook/react torvalds/linux
101
105
  ```
106
+ Multiple URLs clone concurrently (bounded by `parallel_limit`, default 6) — a mismatched remote on an existing path errors instead of silently registering, and duplicate URLs in the same invocation are deduped.
107
+
102
108
  Creates (in a structured workspace):
103
109
  ```
104
110
  ~/oss/
@@ -117,8 +123,9 @@ gitstow -w oss pull # Only update oss workspace
117
123
  gitstow pull --tag ai # Only repos tagged 'ai'
118
124
  gitstow pull --exclude-tag stale # Everything except stale repos
119
125
  gitstow status # Git status dashboard
120
- gitstow -w active status --dirty # Dirty repos in active workspace
126
+ gitstow -w active status --dirty # Repos with local changes in active workspace
121
127
  ```
128
+ `status` shows local changes as a composition (e.g. "2 modified · 1 staged · 3 untracked") rather than a single "dirty" bucket, and keeps the remote relationship (ahead/behind/diverged) in its own column.
122
129
 
123
130
  ### Freeze & Tags
124
131
  ```bash
@@ -182,17 +189,13 @@ gsp # gitstow pull shorthand
182
189
 
183
190
  ### Browser Dashboard
184
191
  ```bash
185
- gitstow ui # opens http://127.0.0.1:7853 in your browser
192
+ pip install "gitstow[ui]" # if you installed CLI-only
193
+ gitstow ui # opens http://127.0.0.1:7853 in your browser
186
194
  gitstow ui --port 8080
187
195
  gitstow ui --no-browser
188
196
  ```
189
197
  Persistent local web dashboard for daily repo management — a tab you leave open. Shows dirty state across your library, pulls single repos or all-of-them with a parallel summary panel, adds/removes repos, edits tags, freezes stale ones. Auto-refreshes row status every 30 seconds. Binds `127.0.0.1` only (arbitrary git execution must not be LAN-reachable). Click **Shutdown** in the footer — or Ctrl+C — to stop.
190
198
 
191
- ### Interactive Dashboard (TUI)
192
- ```bash
193
- gitstow tui # Keyboard-driven terminal dashboard (SSH-friendly)
194
- ```
195
-
196
199
  ### JSON Output
197
200
  Every command supports `--json` for scripting and AI consumption:
198
201
  ```bash
@@ -237,7 +240,6 @@ gitstow status --json
237
240
  | `gitstow shell init` | Print shell functions to source in rc file |
238
241
  | `gitstow shell pick` | fzf-powered repo picker |
239
242
  | `gitstow shell completions` | Tab completion for repo names, workspaces, tags |
240
- | `gitstow tui` | Interactive terminal dashboard |
241
243
  | `gitstow fetch` | Fetch all remotes — update ahead/behind counts without merging |
242
244
  | `gitstow ui` | Launch local browser dashboard (http://127.0.0.1:7853) |
243
245
  | `gitstow update` | Upgrade gitstow itself from PyPI (detects pipx / pip / editable) |
@@ -17,8 +17,8 @@ Existing tools solve parts of this:
17
17
  ## Quick Start
18
18
 
19
19
  ```bash
20
- pipx install gitstow # recommended
21
- # or: pip install gitstow
20
+ pipx install "gitstow[ui]" # recommended — CLI + browser dashboard
21
+ # or: pip install gitstow # CLI only; add [ui] for the dashboard
22
22
 
23
23
  # First-run setup (optional — works without it)
24
24
  gitstow onboard
@@ -54,6 +54,8 @@ gitstow workspace scan active
54
54
  ```bash
55
55
  gitstow add anthropic/claude-code facebook/react torvalds/linux
56
56
  ```
57
+ Multiple URLs clone concurrently (bounded by `parallel_limit`, default 6) — a mismatched remote on an existing path errors instead of silently registering, and duplicate URLs in the same invocation are deduped.
58
+
57
59
  Creates (in a structured workspace):
58
60
  ```
59
61
  ~/oss/
@@ -72,8 +74,9 @@ gitstow -w oss pull # Only update oss workspace
72
74
  gitstow pull --tag ai # Only repos tagged 'ai'
73
75
  gitstow pull --exclude-tag stale # Everything except stale repos
74
76
  gitstow status # Git status dashboard
75
- gitstow -w active status --dirty # Dirty repos in active workspace
77
+ gitstow -w active status --dirty # Repos with local changes in active workspace
76
78
  ```
79
+ `status` shows local changes as a composition (e.g. "2 modified · 1 staged · 3 untracked") rather than a single "dirty" bucket, and keeps the remote relationship (ahead/behind/diverged) in its own column.
77
80
 
78
81
  ### Freeze & Tags
79
82
  ```bash
@@ -137,17 +140,13 @@ gsp # gitstow pull shorthand
137
140
 
138
141
  ### Browser Dashboard
139
142
  ```bash
140
- gitstow ui # opens http://127.0.0.1:7853 in your browser
143
+ pip install "gitstow[ui]" # if you installed CLI-only
144
+ gitstow ui # opens http://127.0.0.1:7853 in your browser
141
145
  gitstow ui --port 8080
142
146
  gitstow ui --no-browser
143
147
  ```
144
148
  Persistent local web dashboard for daily repo management — a tab you leave open. Shows dirty state across your library, pulls single repos or all-of-them with a parallel summary panel, adds/removes repos, edits tags, freezes stale ones. Auto-refreshes row status every 30 seconds. Binds `127.0.0.1` only (arbitrary git execution must not be LAN-reachable). Click **Shutdown** in the footer — or Ctrl+C — to stop.
145
149
 
146
- ### Interactive Dashboard (TUI)
147
- ```bash
148
- gitstow tui # Keyboard-driven terminal dashboard (SSH-friendly)
149
- ```
150
-
151
150
  ### JSON Output
152
151
  Every command supports `--json` for scripting and AI consumption:
153
152
  ```bash
@@ -192,7 +191,6 @@ gitstow status --json
192
191
  | `gitstow shell init` | Print shell functions to source in rc file |
193
192
  | `gitstow shell pick` | fzf-powered repo picker |
194
193
  | `gitstow shell completions` | Tab completion for repo names, workspaces, tags |
195
- | `gitstow tui` | Interactive terminal dashboard |
196
194
  | `gitstow fetch` | Fetch all remotes — update ahead/behind counts without merging |
197
195
  | `gitstow ui` | Launch local browser dashboard (http://127.0.0.1:7853) |
198
196
  | `gitstow update` | Upgrade gitstow itself from PyPI (detects pipx / pip / editable) |
@@ -3,7 +3,7 @@
3
3
  **Date:** 2026-07-06
4
4
  **Auditor:** Claude (Fable 5), full read of all source (~9.5k lines Python), planning docs, tests, CI, packaging
5
5
  **Baseline:** commit `1aefe9a`, 139 tests passing
6
- **Status:** Awaiting greenlight see [Implementation Waves](#implementation-waves)
6
+ **Status:** COMPLETEall three waves shipped (Wave 1 → 0.2.8; Waves 2+3 → 0.3.0)
7
7
 
8
8
  This document is the working tracker for the post-audit improvement effort. Each finding
9
9
  has an ID for reference. Check items off as waves land.
@@ -119,27 +119,27 @@ Opus (substantial) / Sonnet (mechanical) subagents.
119
119
  - [x] B12 path-containment check in CLI `remove --delete`
120
120
  - [x] B11 workspace label validation
121
121
 
122
- ### Wave 2 — Shared status model (Opus) — NOT STARTED
123
- - [ ] P5 retire the Textual TUI (decision: retire; clears the third surface before the refactor)
124
- - [ ] A1/P4 `core/status_model.py`: one classifier separating local composition (modified/staged/untracked counts) from remote relationship (in-sync/ahead/behind/diverged) + frozen/missing overlays
125
- - [ ] Consume from CLI `status`, web dashboard, and `--json` payloads (per CLAUDE.md standards)
126
- - [ ] B6 one pull rule on both surfaces: modified/staged skip, untracked-only pulls (decision locked)
127
- - [ ] E1 parallelize web status gathering via `run_parallel` (off the event loop)
128
- - [ ] E2 batch RepoStore updates — `bulk()` (single save per bulk op)
129
- - [ ] Tests covering the classifier matrix
130
-
131
- ### Wave 3 — Structure & polish (Sonnet-heavy, Opus for A3/A4) — NOT STARTED
132
- - [ ] P1/E3 parallel `add` clones; P2 remote-mismatch conflict error
133
- - [ ] E4 parallel `search`
134
- - [ ] A4 `core/operations.py` extraction (filters + bulk runner); collapse pull/fetch duplication
135
- - [ ] A3 MCP server consumes operations layer; add `last_fetched` everywhere (DOC3); new `fetch_repos` tool
136
- - [ ] P3 reconciliation hints surfacing untracked repos in `list`/`status` (human output only; JSON shapes unchanged)
137
- - [ ] B7 import preserves workspace; loud version error. B8 orphaned-workspace detection in doctor + workspace remove warning
138
- - [ ] B9 `$VISUAL`/`$EDITOR`-first open logic; terminal editors run foreground
139
- - [ ] A2 web stack → `[ui]` extra (decision locked; ships 0.3.0)
140
- - [ ] E5 `du`-backed disk sizing; E6 configurable `clone_timeout` setting
141
- - [ ] D2 macOS CI; D3 changelog gate in release.sh; D4 typer extra; D5 fetch/MCP tests
142
- - [ ] DOC1/DOC2 docs sync
122
+ ### Wave 2 — Shared status model (Opus) — COMPLETE (2026-07-12)
123
+ - [x] P5 retire the Textual TUI (decision: retire; clears the third surface before the refactor)
124
+ - [x] A1/P4 `core/status_model.py`: one classifier separating local composition (modified/staged/untracked counts) from remote relationship (in-sync/ahead/behind/diverged) + frozen/missing overlays
125
+ - [x] Consume from CLI `status`, web dashboard, and `--json` payloads (per CLAUDE.md standards)
126
+ - [x] B6 one pull rule on both surfaces: modified/staged skip, untracked-only pulls (decision locked)
127
+ - [x] E1 parallelize web status gathering via `run_parallel` (off the event loop)
128
+ - [x] E2 batch RepoStore updates — `bulk()` (single save per bulk op)
129
+ - [x] Tests covering the classifier matrix
130
+
131
+ ### Wave 3 — Structure & polish (Sonnet-heavy, Opus for A3/A4) — COMPLETE (2026-07-12)
132
+ - [x] P1/E3 parallel `add` clones; P2 remote-mismatch conflict error
133
+ - [x] E4 parallel `search`
134
+ - [x] A4 `core/operations.py` extraction (filters + bulk runner); collapse pull/fetch duplication
135
+ - [x] A3 MCP server consumes operations layer; add `last_fetched` everywhere (DOC3); new `fetch_repos` tool
136
+ - [x] P3 reconciliation hints surfacing untracked repos in `list`/`status` (human output only; JSON shapes unchanged)
137
+ - [x] B7 import preserves workspace; loud version error. B8 orphaned-workspace detection in doctor + workspace remove warning
138
+ - [x] B9 `$VISUAL`/`$EDITOR`-first open logic; terminal editors run foreground
139
+ - [x] A2 web stack → `[ui]` extra (decision locked; ships 0.3.0)
140
+ - [x] E5 `du`-backed disk sizing; E6 configurable `clone_timeout` setting
141
+ - [x] D2 macOS CI; D3 changelog gate in release.sh; D4 typer extra; D5 fetch/MCP tests
142
+ - [x] DOC1/DOC2 docs sync
143
143
 
144
144
  ## Verification notes
145
145
 
@@ -83,8 +83,10 @@ gitstow add anthropic/claude-code --update
83
83
  - Repos are added to the default workspace (first configured), or the workspace specified with `-w`
84
84
  - If the repo is already tracked: skips (or pulls with `--update`)
85
85
  - If the path exists on disk but isn't tracked: registers it automatically
86
+ - If the path exists on disk with a **different** remote than the one requested: errors with a remote-mismatch message instead of silently registering — move the directory or pick another workspace
86
87
  - If the path exists but isn't a git repo: errors
87
- - Multiple URLs are cloned in parallel
88
+ - Equivalent URLs passed twice in the same invocation (e.g. HTTPS and SSH forms of the same repo) are deduplicated — only the first is cloned, the rest are reported as duplicates
89
+ - Multiple URLs are cloned concurrently (bounded by `parallel_limit`, default 6), with per-clone retry via `--retry`
88
90
 
89
91
  ---
90
92
 
@@ -132,7 +134,9 @@ gitstow pull --include-frozen
132
134
 
133
135
  **Behavior:**
134
136
  - Frozen repos are skipped (shown as "Frozen" in summary)
135
- - Dirty repos are skipped (never risks losing local changes)
137
+ - Repos with modified or staged files are skipped — the composition is shown in the summary (e.g. "2 modified · 1 staged")
138
+ - Diverged repos (local and remote both have commits the other lacks) are skipped — resolve manually (rebase or merge)
139
+ - Repos with only untracked files ARE pulled — an ff-only pull can't lose them
136
140
  - Uses `--ff-only` — won't create merge commits
137
141
  - Runs in parallel (configurable, default 6 concurrent)
138
142
  - Updates `last_pulled` timestamp on success
@@ -217,6 +221,8 @@ gitstow list --paths # Show full paths
217
221
  gitstow list --json # Machine-readable output
218
222
  ```
219
223
 
224
+ If a workspace has repos on disk that aren't tracked yet, `list` prints a hint (e.g. `⚠ 2 untracked repos in oss — run gitstow workspace scan oss`) — suppressed by `--json`/`--quiet`.
225
+
220
226
  ---
221
227
 
222
228
  ### `gitstow status`
@@ -233,19 +239,25 @@ gitstow status [flags]
233
239
  |------|-------|-------------|
234
240
  | `--tag` | `-t` | Filter by tag. |
235
241
  | `--owner` | | Filter by owner. |
236
- | `--dirty` | | Show only dirty repos. |
242
+ | `--dirty` | | Show only repos with local changes. |
237
243
  | `--json` | `-j` | JSON output. |
238
244
  | `--quiet` | `-q` | Minimal output. |
239
245
 
240
- Shows: repo name, branch, clean/dirty status (with file counts), ahead/behind remote, last commit message and date.
246
+ Shows: repo name, branch, a **Local Changes** column (composition e.g. "2 modified · 1 staged · 3 untracked", or "clean"), a separate **Remote** column (in-sync / ahead / behind / diverged / no-upstream), and last commit message and date. Local working-tree state and remote relationship are always shown as two separate dimensions — never a bare "dirty" bucket.
247
+
248
+ **JSON output** (`--json`) keeps all existing flat fields (`dirty`, `staged`, `untracked`, `ahead`, `behind`, `clean`, `status_symbol`, ...) and adds two keys from the shared status model:
249
+ - `local` — `{modified, staged, untracked, summary}`
250
+ - `remote` — `{state, ahead, behind}`, where `state` is one of `in-sync` / `ahead` / `behind` / `diverged` / `no-upstream` / `unknown`
241
251
 
242
- **Status symbols:**
252
+ **Status symbols** (legacy `status_symbol` JSON field):
243
253
  - `✓` clean
244
254
  - `*` unstaged changes
245
255
  - `+` staged changes
246
256
  - `?` untracked files
247
257
  - `❄` frozen
248
258
 
259
+ Like `list`, `status` prints an untracked-repos hint per workspace when repos exist on disk but aren't tracked — suppressed by `--json`/`--quiet`.
260
+
249
261
  ---
250
262
 
251
263
  ### `gitstow remove`
@@ -406,12 +418,12 @@ gitstow open <owner/repo> [flags]
406
418
 
407
419
  | Flag | Short | Description |
408
420
  |------|-------|-------------|
409
- | `--editor` | `-e` | Open in default editor (VS Code, Cursor, etc.). |
421
+ | `--editor` | `-e` | Open in your editor (`$VISUAL`/`$EDITOR`, then VS Code/Cursor). This is the default. |
410
422
  | `--browser` | `-b` | Open the repo on GitHub/GitLab in your browser. |
411
423
  | `--finder` | `-f` | Open in Finder/file manager. |
412
424
  | `--path` | `-p` | Just print the path to stdout. |
413
425
 
414
- With no flags, opens in the default editor. The `--path` flag is useful for shell integration:
426
+ With no flags, opens in an editor: `$VISUAL`, then `$EDITOR`, then a fallback of VS Code (`code`) or Cursor (`cursor`) if installed. Terminal editors (`vi`, `vim`, `nvim`, `nano`, `emacs`, `hx`, `kak`, `micro`) run in the foreground since they need the TTY; GUI editors are launched detached. The `--path` flag is useful for shell integration:
415
427
 
416
428
  ```bash
417
429
  cd "$(gitstow open anthropic/claude-code -p)"
@@ -517,25 +529,12 @@ Completes:
517
529
  - Workspace labels for `-w/--workspace`
518
530
  - Tag names for `-t/--tag`
519
531
 
520
- ### `gitstow tui`
521
-
522
- Interactive terminal dashboard built with [Textual](https://github.com/Textualize/textual). Requires `pip install gitstow[tui]`.
523
-
524
- Keyboard shortcuts:
525
- - `r` — Refresh
526
- - `p` — Pull all unfrozen repos
527
- - `P` — Pull selected repo only
528
- - `f` — Toggle freeze on selected repo
529
- - `w` — Cycle workspace filter
530
- - `t` — Cycle tag filter
531
- - `Enter` — Show repo details
532
- - `/` — Focus filter input
533
- - `q` — Quit
534
-
535
532
  ### `gitstow ui`
536
533
 
537
534
  Launch a local browser dashboard — a dark-themed web UI for daily repo management. Binds to `127.0.0.1` only; auto-opens your default browser.
538
535
 
536
+ > Requires the `ui` extra: `pip install "gitstow[ui]"` (or `pipx install "gitstow[ui]"`). Running `gitstow ui` without it prints an install hint and exits.
537
+
539
538
  ```bash
540
539
  gitstow ui # http://127.0.0.1:7853
541
540
  gitstow ui --port 8080 # custom port
@@ -561,8 +560,8 @@ Every interactive element has a hover tooltip. There's also a `?` button in the
561
560
  | | Meaning | What to do |
562
561
  |---|---|---|
563
562
  | clean | Working tree matches HEAD; in sync with the last fetch | Nothing |
564
- | dirty | Uncommitted local changes | Commit or stash before pulling |
565
- | conflict | Dirty AND behind remote | Resolve locally before pulling |
563
+ | dirty | Local changes — the label shows the composition (e.g. "2 modified · 1 staged · 3 untracked") | Commit/stash modified or staged files before pulling; untracked-only repos can still pull |
564
+ | conflict | Local changes (modified/staged) AND behind remote, or local and remote have diverged | Resolve locally before pulling |
566
565
  | behind | Remote has commits you don't | Click the orange Pull button |
567
566
  | ahead | You have local commits not yet pushed | Consider `git push` |
568
567
  | frozen | Intentionally skipped by "Pull all" | Unfreeze from the ⋯ menu |
@@ -570,8 +569,8 @@ Every interactive element has a hover tooltip. There's also a `?` button in the
570
569
  **The Pull button color encodes priority**:
571
570
 
572
571
  - **Orange** `↓ Pull 5` — repo is behind; clicking fast-forwards by the shown count
573
- - **Ghost** (gray) — already up to date, or working tree is dirty/ahead. The button still works; it just won't change anything useful
574
- - **Disabled** — frozen, in conflict, or missing on disk
572
+ - **Ghost** (gray) — already up to date, has local changes, or ahead. The button still works; it just won't change anything useful. (Untracked-only local changes still pull fine — a bulk pull only skips modified/staged files.)
573
+ - **Disabled** — frozen, in conflict, diverged from remote, or missing on disk
575
574
 
576
575
  **Remote Δ** (`↑ N`, `↓ N`, or `—`) reflects your *last fetch*, not live remote state. If the counts look stale, pull (or run `git fetch --all` manually) to refresh them.
577
576
 
@@ -615,20 +614,26 @@ Change a setting.
615
614
  gitstow config set default_host gitlab.com
616
615
  gitstow config set prefer_ssh true
617
616
  gitstow config set parallel_limit 8
617
+ gitstow config set clone_timeout 900
618
618
  ```
619
619
 
620
+ Valid keys: `default_host`, `prefer_ssh`, `parallel_limit`, `clone_timeout` (clone timeout in seconds).
621
+
620
622
  > **Note:** The old `root_path` setting has been replaced by workspaces. Use `gitstow workspace add` to manage where repos are stored.
621
623
 
622
624
  ### `gitstow config migrate-root`
623
625
 
624
- Move all repos to a new root directory.
626
+ Move one workspace's repos to a new directory and update that workspace's `path` in config.
625
627
 
626
628
  ```bash
627
- gitstow config migrate-root ~/new-location
628
- gitstow config migrate-root ~/new-location --copy # Keep originals
629
- gitstow config migrate-root ~/new-location --yes # Skip confirmation
629
+ gitstow config migrate-root ~/new-location # Moves the default workspace
630
+ gitstow -w active config migrate-root ~/new-location # Moves a specific workspace
631
+ gitstow config migrate-root ~/new-location --copy # Keep originals
632
+ gitstow config migrate-root ~/new-location --yes # Skip confirmation
630
633
  ```
631
634
 
635
+ > **Note:** `migrate-root` has no local `--workspace` flag — target a non-default workspace with the global `-w` flag *before* `config`, not after `migrate-root`.
636
+
632
637
  ### `gitstow config path`
633
638
 
634
639
  Print the config file path.
@@ -774,13 +779,14 @@ Then add to your AI tool's config:
774
779
 
775
780
  Or use `gitstow setup-ai` to auto-detect and configure.
776
781
 
777
- ### MCP Tools (12)
782
+ ### MCP Tools (14)
778
783
 
779
784
  | Tool | Description |
780
785
  |------|-------------|
781
786
  | `list_repos` | List repos with tag/owner/query filters |
782
787
  | `add_repo` | Clone a repo (shorthand or full URL) |
783
788
  | `pull_repos` | Bulk pull with tag/exclude/frozen filters |
789
+ | `fetch_repos` | Bulk fetch (updates ahead/behind, no merge; includes frozen) |
784
790
  | `repo_status` | Git status dashboard across repos |
785
791
  | `repo_info` | Detailed single repo info |
786
792
  | `freeze_repo` | Freeze a repo (skip during pull) |
@@ -790,6 +796,7 @@ Or use `gitstow setup-ai` to auto-detect and configure.
790
796
  | `remove_repo` | Remove from tracking (optionally delete) |
791
797
  | `search_repos` | Grep across repos with pattern and glob |
792
798
  | `collection_stats` | Disk usage, owner breakdown, tag counts |
799
+ | `list_workspaces` | List configured workspaces with repo counts |
793
800
 
794
801
  ### MCP Resources (3)
795
802
 
@@ -22,10 +22,13 @@ Get gitstow installed and managing repos in about 5 minutes.
22
22
  pip install gitstow
23
23
  ```
24
24
 
25
+ Want the browser dashboard (`gitstow ui`) too? Add the `[ui]` extra: `pip install "gitstow[ui]"`.
26
+
25
27
  Or with [pipx](https://pipx.pypa.io/) (recommended — keeps it isolated):
26
28
 
27
29
  ```bash
28
- pipx install gitstow
30
+ pipx install "gitstow[ui]" # CLI + browser dashboard
31
+ # or: pipx install gitstow # CLI only
29
32
  ```
30
33
 
31
34
  Verify it worked:
@@ -163,6 +166,7 @@ This is also done automatically during `gitstow onboard` and auto-updates when y
163
166
  ## Prefer a Browser?
164
167
 
165
168
  ```bash
169
+ pip install "gitstow[ui]" # only needed if you installed CLI-only above
166
170
  gitstow ui
167
171
  ```
168
172
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "gitstow"
7
- version = "0.2.8"
7
+ version = "0.3.0"
8
8
  description = "A git repository library manager — clone, organize, and maintain collections of repos you learn from"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -30,14 +30,10 @@ classifiers = [
30
30
  ]
31
31
 
32
32
  dependencies = [
33
- "typer[all]>=0.9",
33
+ "typer>=0.9",
34
34
  "pyyaml>=6.0",
35
35
  "rich>=13.0",
36
36
  "beaupy>=3.0",
37
- "fastapi>=0.110",
38
- "uvicorn>=0.27",
39
- "jinja2>=3.1",
40
- "python-multipart>=0.0.9",
41
37
  ]
42
38
 
43
39
  [project.urls]
@@ -48,13 +44,21 @@ Issues = "https://github.com/rishmadaan/gitstow/issues"
48
44
  Changelog = "https://github.com/rishmadaan/gitstow/blob/main/CHANGELOG.md"
49
45
 
50
46
  [project.optional-dependencies]
47
+ ui = [
48
+ "fastapi>=0.110",
49
+ "uvicorn>=0.27",
50
+ "jinja2>=3.1",
51
+ "python-multipart>=0.0.9",
52
+ ]
51
53
  dev = [
52
54
  "pytest>=8.0",
53
55
  "ruff>=0.4",
54
56
  "httpx>=0.27",
55
- ]
56
- tui = [
57
- "textual>=0.50",
57
+ "fastapi>=0.110",
58
+ "uvicorn>=0.27",
59
+ "jinja2>=3.1",
60
+ "python-multipart>=0.0.9",
61
+ "mcp>=1.0",
58
62
  ]
59
63
  mcp = [
60
64
  "mcp>=1.0",