gitstow 0.2.8__tar.gz → 0.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.
Files changed (124) hide show
  1. {gitstow-0.2.8 → gitstow-0.4.0}/.github/workflows/ci.yml +5 -1
  2. gitstow-0.4.0/BACKLOG.md +43 -0
  3. {gitstow-0.2.8 → gitstow-0.4.0}/CHANGELOG.md +54 -0
  4. {gitstow-0.2.8 → gitstow-0.4.0}/CLAUDE.md +19 -18
  5. {gitstow-0.2.8 → gitstow-0.4.0}/PKG-INFO +20 -18
  6. {gitstow-0.2.8 → gitstow-0.4.0}/README.md +8 -10
  7. {gitstow-0.2.8 → gitstow-0.4.0}/docs/building/audit-2026-07-06.md +22 -22
  8. gitstow-0.4.0/docs/building/plans/2026-07-12-ui-wave-a-make-it-real.md +831 -0
  9. gitstow-0.4.0/docs/building/plans/2026-07-12-ui-wave-b-polish.md +455 -0
  10. gitstow-0.4.0/docs/building/ui-audit-2026-07-12.md +57 -0
  11. {gitstow-0.2.8 → gitstow-0.4.0}/docs/user/commands.md +39 -32
  12. {gitstow-0.2.8 → gitstow-0.4.0}/docs/user/getting-started.md +5 -1
  13. {gitstow-0.2.8 → gitstow-0.4.0}/pyproject.toml +13 -9
  14. {gitstow-0.2.8 → gitstow-0.4.0}/scripts/release.sh +6 -0
  15. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/__init__.py +1 -1
  16. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/add.py +100 -34
  17. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/config_cmd.py +6 -4
  18. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/doctor.py +17 -0
  19. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/exec_cmd.py +4 -1
  20. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/export_cmd.py +38 -70
  21. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/fetch.py +31 -68
  22. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/helpers.py +22 -0
  23. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/list_cmd.py +4 -1
  24. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/main.py +0 -2
  25. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/manage.py +4 -1
  26. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/onboard.py +2 -1
  27. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/open_cmd.py +25 -11
  28. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/pull.py +52 -74
  29. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/search.py +43 -30
  30. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/serve.py +3 -2
  31. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/status.py +43 -30
  32. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/workspace_cmd.py +9 -0
  33. gitstow-0.4.0/src/gitstow/core/collection_io.py +98 -0
  34. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/config.py +3 -0
  35. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/discovery.py +11 -7
  36. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/git.py +22 -5
  37. gitstow-0.4.0/src/gitstow/core/operations.py +98 -0
  38. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/repo.py +32 -1
  39. gitstow-0.4.0/src/gitstow/core/status_model.py +126 -0
  40. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/mcp/server.py +85 -43
  41. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/skill/SKILL.md +5 -3
  42. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/routes/collection.py +19 -74
  43. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/routes/dashboard.py +101 -37
  44. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/routes/pages.py +76 -8
  45. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/routes/repos.py +60 -28
  46. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/server.py +2 -2
  47. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/static/app.css +105 -9
  48. gitstow-0.4.0/src/gitstow/web/static/dashboard.js +97 -0
  49. gitstow-0.4.0/src/gitstow/web/static/fonts/bricolage-grotesque-latin-ext-var.woff2 +0 -0
  50. gitstow-0.4.0/src/gitstow/web/static/fonts/bricolage-grotesque-latin-var.woff2 +0 -0
  51. gitstow-0.4.0/src/gitstow/web/static/fonts/fonts.css +81 -0
  52. gitstow-0.4.0/src/gitstow/web/static/fonts/jetbrains-mono-latin-ext-var.woff2 +0 -0
  53. gitstow-0.4.0/src/gitstow/web/static/fonts/jetbrains-mono-latin-var.woff2 +0 -0
  54. gitstow-0.4.0/src/gitstow/web/static/vendor/VENDORED.md +39 -0
  55. gitstow-0.4.0/src/gitstow/web/static/vendor/htmx.min.js +1 -0
  56. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/_repo_drawer.html +5 -12
  57. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/add_repo.html +20 -2
  58. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/base.html +73 -1
  59. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/dashboard.html +25 -18
  60. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/partials/pull_summary.html +12 -1
  61. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/partials/repo_row.html +6 -6
  62. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/settings.html +46 -13
  63. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/workspaces.html +4 -2
  64. gitstow-0.4.0/tests/test_cli.py +902 -0
  65. gitstow-0.4.0/tests/test_collection_io.py +33 -0
  66. {gitstow-0.2.8 → gitstow-0.4.0}/tests/test_config.py +9 -0
  67. {gitstow-0.2.8 → gitstow-0.4.0}/tests/test_git.py +22 -0
  68. gitstow-0.4.0/tests/test_mcp.py +138 -0
  69. gitstow-0.4.0/tests/test_operations.py +67 -0
  70. {gitstow-0.2.8 → gitstow-0.4.0}/tests/test_repo.py +51 -0
  71. gitstow-0.4.0/tests/test_serve.py +962 -0
  72. gitstow-0.4.0/tests/test_status_model.py +87 -0
  73. gitstow-0.2.8/BACKLOG.md +0 -18
  74. gitstow-0.2.8/src/gitstow/cli/tui.py +0 -27
  75. gitstow-0.2.8/src/gitstow/tui/__init__.py +0 -1
  76. gitstow-0.2.8/src/gitstow/tui/app.py +0 -384
  77. gitstow-0.2.8/tests/test_cli.py +0 -266
  78. gitstow-0.2.8/tests/test_serve.py +0 -435
  79. {gitstow-0.2.8 → gitstow-0.4.0}/.claude/settings.json +0 -0
  80. {gitstow-0.2.8 → gitstow-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  81. {gitstow-0.2.8 → gitstow-0.4.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  82. {gitstow-0.2.8 → gitstow-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  83. {gitstow-0.2.8 → gitstow-0.4.0}/.github/workflows/publish.yml +0 -0
  84. {gitstow-0.2.8 → gitstow-0.4.0}/.gitignore +0 -0
  85. {gitstow-0.2.8 → gitstow-0.4.0}/AGENTS.md +0 -0
  86. {gitstow-0.2.8 → gitstow-0.4.0}/CODE_OF_CONDUCT.md +0 -0
  87. {gitstow-0.2.8 → gitstow-0.4.0}/CONTRIBUTING.md +0 -0
  88. {gitstow-0.2.8 → gitstow-0.4.0}/LICENSE +0 -0
  89. {gitstow-0.2.8 → gitstow-0.4.0}/SECURITY.md +0 -0
  90. {gitstow-0.2.8 → gitstow-0.4.0}/demo.gif +0 -0
  91. {gitstow-0.2.8 → gitstow-0.4.0}/demo.tape +0 -0
  92. {gitstow-0.2.8 → gitstow-0.4.0}/docs/building/implementation-plan.md +0 -0
  93. {gitstow-0.2.8 → gitstow-0.4.0}/docs/building/plans/2026-07-06-wave-1-correctness-safety.md +0 -0
  94. {gitstow-0.2.8 → gitstow-0.4.0}/docs/building/plans/2026-07-06-wave-2-status-model.md +0 -0
  95. {gitstow-0.2.8 → gitstow-0.4.0}/docs/building/plans/2026-07-06-wave-3-structure-polish.md +0 -0
  96. {gitstow-0.2.8 → gitstow-0.4.0}/docs/user/concepts.md +0 -0
  97. {gitstow-0.2.8 → gitstow-0.4.0}/docs/user/configuration.md +0 -0
  98. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/__main__.py +0 -0
  99. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/__init__.py +0 -0
  100. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/migrate.py +0 -0
  101. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/remove.py +0 -0
  102. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/setup_ai.py +0 -0
  103. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/shell.py +0 -0
  104. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/skill_cmd.py +0 -0
  105. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/stats.py +0 -0
  106. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/cli/update.py +0 -0
  107. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/__init__.py +0 -0
  108. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/locking.py +0 -0
  109. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/parallel.py +0 -0
  110. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/paths.py +0 -0
  111. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/core/url_parser.py +0 -0
  112. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/mcp/__init__.py +0 -0
  113. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/__init__.py +0 -0
  114. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/routes/__init__.py +0 -0
  115. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/routes/system.py +0 -0
  116. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/routes/workspaces.py +0 -0
  117. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/partials/dashboard_rows.html +0 -0
  118. {gitstow-0.2.8 → gitstow-0.4.0}/src/gitstow/web/templates/partials/fetch_summary.html +0 -0
  119. {gitstow-0.2.8 → gitstow-0.4.0}/tests/__init__.py +0 -0
  120. {gitstow-0.2.8 → gitstow-0.4.0}/tests/conftest.py +0 -0
  121. {gitstow-0.2.8 → gitstow-0.4.0}/tests/test_locking.py +0 -0
  122. {gitstow-0.2.8 → gitstow-0.4.0}/tests/test_onboard.py +0 -0
  123. {gitstow-0.2.8 → gitstow-0.4.0}/tests/test_setup_ai.py +0 -0
  124. {gitstow-0.2.8 → gitstow-0.4.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
@@ -0,0 +1,43 @@
1
+ # Backlog
2
+
3
+ Post-v0.1.0 improvements tracked here. See [GitHub Issues](https://github.com/rishmadaan/gitstow/issues) for discussion.
4
+
5
+ ## Enhancements
6
+
7
+ - [x] **TUI: expand beyond read-only dashboard** (#1) — Added pull selected (P), workspace cycling (w), tag cycling (t)
8
+ - [x] **Shell completion for repo names** (#2) — `gitstow shell completions` for bash/zsh/fish; `--quiet` on list/tags/workspace list
9
+ - [x] **Network retry/resume for batch operations** (#3) — `--retry N` flag on add and pull; cleans up partial clones before retry
10
+ - [x] **Export format versioning and checksums** (#4) — Added `version: 1` to YAML/JSON exports; validates on import; backward-compatible with unversioned files
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/publish.yml`); publish on GitHub release
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
+ - [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
+
16
+ ## Documentation
17
+
18
+ - [x] **Record demo GIF for README** (#7) — Recorded with VHS, embedded in README
19
+
20
+ ## Post-0.3.0 follow-ups
21
+
22
+ Triaged non-blocking items from the 2026-07 audit remediation (see
23
+ [docs/building/audit-2026-07-06.md](docs/building/audit-2026-07-06.md) for the full effort).
24
+
25
+ - [ ] **URL parser: schemeless multi-dot hosts** — `dev.azure.com/org/project/_git/repo` without
26
+ `https://` parses as owner/repo shorthand and builds a garbage clone URL (`_LOOKS_LIKE_HOST`
27
+ regex can't match a multi-dot first segment). Pre-existing; 0.3.1 candidate.
28
+ - [ ] **Route search subprocesses through a hardened runner** — `git grep`/`rg` in `cli/search.py`
29
+ and `mcp/server.py` shell raw `subprocess.run` without the `GIT_TERMINAL_PROMPT=0`/`LC_ALL=C`
30
+ env applied to all other git calls. Local-only operations, negligible risk — consistency cleanup.
31
+ - [ ] **Test strengthening batch** — frozen-collapse MCP test needs two frozen same-named repos;
32
+ `run_bulk` missing-status retry + `on_attempt` callback untested; locking mutual-exclusion test
33
+ passes without a lock (use an event-based barrier); status JSON legacy-key test covers 3/13 keys.
34
+ - [ ] **Small edges** — `add` on an on-disk repo with no remote falls through to a doomed clone;
35
+ whitespace-only `$EDITOR` raises IndexError; fully-untracked workspaces never get the untracked
36
+ hint (early return; `doctor` covers); dashboard vs single-row-refresh number sources diverge for
37
+ orphaned-workspace repos.
38
+ - [ ] **Product call: `pull --force` escape hatch** — CLI explicit args follow the bulk skip rule
39
+ while a web single-row Pull click pulls unconditionally; a `--force` flag would reconcile the
40
+ "explicit intent" asymmetry.
41
+ - [ ] **Product call: frozen + diverged + `--include-frozen`** — surfaces as an ff-only error row
42
+ instead of a clean "diverged" skip (check `remote_state` directly in the worker if the messaging
43
+ matters).
@@ -4,6 +4,60 @@ 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.4.0] - 2026-07-12
8
+
9
+ ### Added
10
+
11
+ - **Dashboard filters that actually filter.** The search box, workspace dropdown, and Hide-frozen toggle now work — instant client-side filtering that survives auto-refresh and pull updates.
12
+ - **Settings save.** The Settings page persists changes for real (and gains Parallel limit + Clone timeout fields).
13
+ - **`local` badge.** Repos without an upstream remote show a `local` Remote Δ badge; bulk pulls skip them with a clear reason instead of failing every run.
14
+ - **Responsive dashboard.** Works down to half-monitor width — columns collapse by priority, the table scrolls within its own container, and row menus flip upward when space runs out.
15
+ - **Last-fetched visibility.** The repo detail page shows when each repo was last pulled AND last fetched (hover for exact times); Remote Δ tooltips state which fetch the counts reflect.
16
+
17
+ ### Changed
18
+
19
+ - **Fully offline dashboard.** htmx and both fonts are now bundled — no CDN, no network needed for the UI itself.
20
+ - **Styled confirmations.** Native browser confirm/alert dialogs replaced with an in-app dialog (also makes the dashboard automatable).
21
+ - **Web collection import honors recorded workspaces**, sharing one implementation with the CLI.
22
+ - **Honest metrics chips.** Diverged and missing repos get their own hero chips instead of hiding inside 'conflict'; the pull summary reads 'N attempted' with exclusions stated.
23
+ - **Cleaner micro-interactions.** Styled file picker, a LIVE dot that actually goes red when the server is unreachable, keyboard-visible focus rings, Escape closes menus, add-repo shows a Cloning… state.
24
+
25
+ ### Fixed
26
+
27
+ - **Pull all / Fetch all spinners stop when the operation completes** (htmx indicator/disable double-count).
28
+ - **Repo detail timestamps** no longer render raw machine format.
29
+
30
+ ## [0.3.0] - 2026-07-12
31
+
32
+ ### Added
33
+
34
+ - **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).
35
+ - **`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.
36
+ - **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.
37
+ - **`doctor` detects orphaned workspaces**, and `workspace remove` now warns when repos would be left pointing at a removed workspace.
38
+ - **`clone_timeout` setting** (`gitstow config set clone_timeout <seconds>`) — clone operations on very large repos no longer fail on a hardcoded 300s limit.
39
+ - **macOS added to the CI test matrix**, matching the packaging classifiers.
40
+ - **CHANGELOG gate in the release script** — `scripts/release.sh` now refuses to cut a release without a corresponding changelog entry.
41
+
42
+ ### Changed
43
+
44
+ - **`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.
45
+ - **`search` now runs across repos in parallel**, matching `exec`.
46
+ - **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).
47
+ - **`open` prefers `$VISUAL`/`$EDITOR`** over guessing an editor, and runs terminal editors in the foreground so they actually get a usable TTY.
48
+ - **Disk sizing (`stats`, `repo info`) now shells out to `du`** instead of walking every file in Python — much faster on large repos.
49
+ - **`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.
50
+ - **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.
51
+ - **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.
52
+
53
+ ### Fixed
54
+
55
+ - **`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.
56
+
57
+ ### Removed
58
+
59
+ - **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.
60
+
7
61
  ## [0.2.8] - 2026-07-12
8
62
 
9
63
  ### 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.4.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