gitstow 0.2.6__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.6 → gitstow-0.3.0}/.github/workflows/ci.yml +5 -1
  2. {gitstow-0.2.6 → gitstow-0.3.0}/.github/workflows/publish.yml +19 -0
  3. gitstow-0.3.0/AGENTS.md +150 -0
  4. {gitstow-0.2.6 → gitstow-0.3.0}/BACKLOG.md +2 -2
  5. {gitstow-0.2.6 → gitstow-0.3.0}/CHANGELOG.md +60 -0
  6. {gitstow-0.2.6 → gitstow-0.3.0}/CLAUDE.md +27 -17
  7. {gitstow-0.2.6 → gitstow-0.3.0}/PKG-INFO +20 -18
  8. {gitstow-0.2.6 → gitstow-0.3.0}/README.md +8 -10
  9. gitstow-0.3.0/docs/building/audit-2026-07-06.md +149 -0
  10. gitstow-0.3.0/docs/building/plans/2026-07-06-wave-1-correctness-safety.md +1301 -0
  11. gitstow-0.3.0/docs/building/plans/2026-07-06-wave-2-status-model.md +1097 -0
  12. gitstow-0.3.0/docs/building/plans/2026-07-06-wave-3-structure-polish.md +1690 -0
  13. {gitstow-0.2.6 → gitstow-0.3.0}/docs/user/commands.md +38 -31
  14. {gitstow-0.2.6 → gitstow-0.3.0}/docs/user/getting-started.md +5 -1
  15. {gitstow-0.2.6 → gitstow-0.3.0}/pyproject.toml +13 -9
  16. {gitstow-0.2.6 → gitstow-0.3.0}/scripts/release.sh +6 -0
  17. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/__init__.py +1 -1
  18. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/add.py +100 -34
  19. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/config_cmd.py +44 -41
  20. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/doctor.py +17 -0
  21. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/exec_cmd.py +4 -1
  22. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/export_cmd.py +75 -21
  23. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/fetch.py +33 -70
  24. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/helpers.py +22 -0
  25. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/list_cmd.py +4 -1
  26. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/main.py +0 -2
  27. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/manage.py +42 -34
  28. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/onboard.py +17 -7
  29. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/open_cmd.py +25 -11
  30. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/pull.py +55 -82
  31. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/remove.py +11 -0
  32. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/search.py +43 -30
  33. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/serve.py +3 -2
  34. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/setup_ai.py +4 -4
  35. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/status.py +43 -30
  36. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/workspace_cmd.py +24 -0
  37. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/core/config.py +3 -0
  38. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/core/discovery.py +11 -7
  39. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/core/git.py +31 -6
  40. gitstow-0.3.0/src/gitstow/core/locking.py +63 -0
  41. gitstow-0.3.0/src/gitstow/core/operations.py +98 -0
  42. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/core/repo.py +81 -34
  43. gitstow-0.3.0/src/gitstow/core/status_model.py +119 -0
  44. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/core/url_parser.py +33 -10
  45. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/mcp/server.py +85 -43
  46. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/skill/SKILL.md +5 -3
  47. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/routes/collection.py +5 -1
  48. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/routes/dashboard.py +57 -28
  49. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/routes/pages.py +10 -1
  50. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/routes/repos.py +53 -26
  51. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/server.py +30 -1
  52. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/_repo_drawer.html +1 -9
  53. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/dashboard.html +7 -7
  54. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/partials/pull_summary.html +11 -0
  55. gitstow-0.3.0/tests/test_cli.py +885 -0
  56. {gitstow-0.2.6 → gitstow-0.3.0}/tests/test_config.py +72 -0
  57. {gitstow-0.2.6 → gitstow-0.3.0}/tests/test_git.py +36 -0
  58. gitstow-0.3.0/tests/test_locking.py +64 -0
  59. gitstow-0.3.0/tests/test_mcp.py +138 -0
  60. gitstow-0.3.0/tests/test_onboard.py +122 -0
  61. gitstow-0.3.0/tests/test_operations.py +67 -0
  62. {gitstow-0.2.6 → gitstow-0.3.0}/tests/test_repo.py +53 -2
  63. {gitstow-0.2.6 → gitstow-0.3.0}/tests/test_serve.py +189 -1
  64. gitstow-0.3.0/tests/test_setup_ai.py +49 -0
  65. gitstow-0.3.0/tests/test_status_model.py +83 -0
  66. {gitstow-0.2.6 → gitstow-0.3.0}/tests/test_url_parser.py +35 -0
  67. gitstow-0.2.6/src/gitstow/cli/tui.py +0 -27
  68. gitstow-0.2.6/src/gitstow/tui/__init__.py +0 -1
  69. gitstow-0.2.6/src/gitstow/tui/app.py +0 -384
  70. gitstow-0.2.6/tests/test_cli.py +0 -80
  71. {gitstow-0.2.6 → gitstow-0.3.0}/.claude/settings.json +0 -0
  72. {gitstow-0.2.6 → gitstow-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  73. {gitstow-0.2.6 → gitstow-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  74. {gitstow-0.2.6 → gitstow-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  75. {gitstow-0.2.6 → gitstow-0.3.0}/.gitignore +0 -0
  76. {gitstow-0.2.6 → gitstow-0.3.0}/CODE_OF_CONDUCT.md +0 -0
  77. {gitstow-0.2.6 → gitstow-0.3.0}/CONTRIBUTING.md +0 -0
  78. {gitstow-0.2.6 → gitstow-0.3.0}/LICENSE +0 -0
  79. {gitstow-0.2.6 → gitstow-0.3.0}/SECURITY.md +0 -0
  80. {gitstow-0.2.6 → gitstow-0.3.0}/demo.gif +0 -0
  81. {gitstow-0.2.6 → gitstow-0.3.0}/demo.tape +0 -0
  82. {gitstow-0.2.6 → gitstow-0.3.0}/docs/building/implementation-plan.md +0 -0
  83. {gitstow-0.2.6 → gitstow-0.3.0}/docs/user/concepts.md +0 -0
  84. {gitstow-0.2.6 → gitstow-0.3.0}/docs/user/configuration.md +0 -0
  85. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/__main__.py +0 -0
  86. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/__init__.py +0 -0
  87. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/migrate.py +0 -0
  88. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/shell.py +0 -0
  89. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/skill_cmd.py +0 -0
  90. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/stats.py +0 -0
  91. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/cli/update.py +0 -0
  92. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/core/__init__.py +0 -0
  93. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/core/parallel.py +0 -0
  94. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/core/paths.py +0 -0
  95. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/mcp/__init__.py +0 -0
  96. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/__init__.py +0 -0
  97. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/routes/__init__.py +0 -0
  98. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/routes/system.py +0 -0
  99. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/routes/workspaces.py +0 -0
  100. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/static/app.css +0 -0
  101. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/add_repo.html +0 -0
  102. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/base.html +0 -0
  103. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/partials/dashboard_rows.html +0 -0
  104. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/partials/fetch_summary.html +0 -0
  105. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/partials/repo_row.html +0 -0
  106. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/settings.html +0 -0
  107. {gitstow-0.2.6 → gitstow-0.3.0}/src/gitstow/web/templates/workspaces.html +0 -0
  108. {gitstow-0.2.6 → gitstow-0.3.0}/tests/__init__.py +0 -0
  109. {gitstow-0.2.6 → gitstow-0.3.0}/tests/conftest.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
@@ -5,7 +5,26 @@ on:
5
5
  tags: ["v*"]
6
6
 
7
7
  jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.12"
16
+
17
+ - name: Install dependencies
18
+ run: pip install -e ".[dev]"
19
+
20
+ - name: Lint
21
+ run: ruff check src/
22
+
23
+ - name: Test
24
+ run: pytest --tb=short
25
+
8
26
  publish:
27
+ needs: test
9
28
  runs-on: ubuntu-latest
10
29
  environment: pypi
11
30
  permissions:
@@ -0,0 +1,150 @@
1
+ # gitstow - AI Developer Guide
2
+
3
+ ## What is this?
4
+
5
+ A CLI tool that manages collections of git repositories across multiple workspaces. Think "package manager for repos" - whether they're open-source projects you learn from or active projects you work on.
6
+
7
+ ## Operating Contract
8
+
9
+ - 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.
10
+ - Do not recommend quick fixes, temporary patches, or narrow workarounds unless the user explicitly asks for a shortcut.
11
+ - Read the relevant existing code before proposing or editing. Let the current architecture decide where the change belongs.
12
+ - Keep changes scoped to the requested behavior. Avoid unrelated refactors, formatting churn, or drive-by cleanups.
13
+ - Do not revert user changes or generated local state unless the user explicitly asks.
14
+
15
+ ## Product Standards
16
+
17
+ - Keep CLI, TUI, web dashboard, JSON output, docs, and tests semantically aligned when changing user-facing status behavior.
18
+ - 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.
19
+ - Keep local working-tree state separate from remote relationship state. For example: local changes, clean, ahead, behind, diverged, frozen, missing.
20
+ - 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.
21
+
22
+ ## Key Concept: Workspaces
23
+
24
+ Repos are organized into **workspaces** - directories with a label and layout mode:
25
+ - **structured** layout: `workspace/owner/repo/` (open-source collections)
26
+ - **flat** layout: `workspace/repo/` (active projects, no owner subdirectory)
27
+
28
+ Each workspace can have auto-tags applied to all repos it discovers.
29
+
30
+ ## Architecture
31
+
32
+ ```
33
+ src/gitstow/
34
+ ├── cli/ # Typer commands (thin — delegates to core)
35
+ │ ├── main.py # App entry, command registration, global --workspace flag
36
+ │ ├── helpers.py # Shared workspace resolution, repo lookup, iteration
37
+ │ ├── add.py, pull.py, list_cmd.py, status.py, remove.py # Core commands
38
+ │ ├── workspace_cmd.py # workspace list/add/remove/scan subcommands
39
+ │ ├── manage.py # freeze/unfreeze/tag/untag/info subcommands
40
+ │ ├── exec_cmd.py, search.py, open_cmd.py, stats.py # Power commands
41
+ │ ├── export_cmd.py # export/import collection
42
+ │ ├── shell.py # Shell integration (fzf, aliases)
43
+ │ ├── tui.py # TUI launcher
44
+ │ ├── serve.py # Web dashboard launcher
45
+ │ ├── migrate.py, config_cmd.py, onboard.py, doctor.py, skill_cmd.py
46
+ │ └── __init__.py
47
+ ├── core/ # Business logic (git ops, URL parsing, state)
48
+ │ ├── paths.py # Path constants, central repos.yaml resolution
49
+ │ ├── config.py # Workspace + Settings dataclasses, load/save
50
+ │ ├── repo.py # Repo dataclass + RepoStore (nested YAML CRUD)
51
+ │ ├── url_parser.py # URL → (host, owner, repo) extraction
52
+ │ ├── git.py # All git subprocess calls
53
+ │ ├── discovery.py # Walk directory tree (structured + flat), reconcile
54
+ │ ├── parallel.py # Async execution with semaphore
55
+ │ └── __init__.py
56
+ ├── tui/ # Textual interactive dashboard
57
+ │ ├── app.py # Main TUI application
58
+ │ └── __init__.py
59
+ ├── web/ # FastAPI browser dashboard (gitstow ui)
60
+ │ ├── server.py # FastAPI app, uvicorn runner, app.state.server stash
61
+ │ ├── static/app.css # Dark theme, Bricolage Grotesque + JetBrains Mono
62
+ │ ├── templates/ # Jinja2 — base.html + page templates + partials/
63
+ │ └── routes/ # dashboard.py, repos.py, workspaces.py, collection.py, pages.py, system.py
64
+ └── skill/ # Codex skill (SKILL.md)
65
+ └── SKILL.md
66
+ ```
67
+
68
+ **Key rules:**
69
+ 1. CLI never touches git directly - it calls `core/git.py`
70
+ 2. `core/repo.py` (RepoStore) is the only module that reads/writes `repos.yaml`
71
+ 3. `core/config.py` defines Workspace dataclass - all workspace logic flows from here
72
+ 4. `cli/helpers.py` provides `resolve_workspaces()`, `resolve_repo()`, `iter_repos_with_workspace()`
73
+
74
+ ## Key Files
75
+
76
+ - `core/config.py` - Workspace + Settings dataclasses. `get_workspaces()` with legacy migration shim.
77
+ - `core/repo.py` - Repo with workspace field, RepoStore with nested YAML format, legacy auto-migration.
78
+ - `core/discovery.py` - `discover_repos(root, layout)` supports structured and flat layouts.
79
+ - `core/url_parser.py` - URL parsing (the hardest part). Test changes here thoroughly.
80
+ - `core/git.py` - All git subprocess calls. Uses `git status --porcelain=v2 --branch` for single-call efficiency.
81
+ - `core/parallel.py` - Async execution with semaphore (max 6 concurrent).
82
+ - `cli/helpers.py` - Shared workspace resolution used by all CLI commands.
83
+ - `cli/workspace_cmd.py` - workspace list/add/remove/scan subcommands.
84
+ - `cli/main.py` - Typer app, global `-w/--workspace` option, command registration.
85
+ - `tui/app.py` - Textual dashboard with DataTable, filter, pull, freeze toggle.
86
+
87
+ ## Data Files
88
+
89
+ - `~/.gitstow/config.yaml` - Settings (workspaces list, default host, SSH pref).
90
+ - `~/.gitstow/repos.yaml` - Repo metadata nested by workspace label. Central location.
91
+
92
+ ### repos.yaml format
93
+ ```yaml
94
+ oss:
95
+ anthropic/Codex:
96
+ remote_url: https://github.com/anthropic/Codex.git
97
+ tags: [ai]
98
+ active:
99
+ gitstow:
100
+ remote_url: https://github.com/rishmadaan/gitstow.git
101
+ tags: [active]
102
+ ```
103
+
104
+ ## All Commands (32)
105
+
106
+ **Core:** `add`, `pull`, `fetch`, `list`, `status`, `remove`, `migrate`
107
+ **Workspace:** `workspace list`, `workspace add`, `workspace remove`, `workspace scan`
108
+ **Repo management:** `repo freeze`, `repo unfreeze`, `repo tag`, `repo untag`, `repo tags`, `repo info`
109
+ **Power:** `exec`, `search`, `open`, `stats`
110
+ **Sharing:** `collection export`, `collection import`
111
+ **Shell:** `shell pick`, `shell init`, `shell completions`, `shell setup`, `tui`, `ui`
112
+ **Setup:** `onboard`, `config show/set/path/migrate-root`, `doctor`, `install-skill`, `setup-ai`, `update`
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ cd ~/labs/projects/gitstow
118
+ pip install -e ".[dev]"
119
+ pytest # 44 tests
120
+ ruff check src/
121
+ pip install -e ".[tui]" # For TUI development
122
+ ```
123
+
124
+ ## Patterns
125
+
126
+ - `--json -j` and `--quiet -q` on all main commands
127
+ - Global `-w/--workspace` flag filters all commands to a single workspace
128
+ - `cli/helpers.py` for workspace resolution (don't repeat in each command)
129
+ - Rich console for stdout, err_console for stderr
130
+ - Typer with `rich_markup_mode="rich"`, `typer.Context` for global options
131
+ - YAML for persistence (not JSON, not SQLite)
132
+ - asyncio with semaphore for parallel git ops
133
+ - `git status --porcelain=v2 --branch` for single-call status (vs gita's 4-5 calls)
134
+ - Repo.global_key (`workspace:key`) for unique identification across workspaces
135
+ - Legacy format auto-migration (flat repos.yaml -> nested, root_path -> workspaces)
136
+
137
+ ## AI Integration
138
+
139
+ **Primary: Codex skill** (`src/gitstow/skill/SKILL.md`)
140
+ - Installed to `~/.Codex/skills/gitstow/` via `gitstow install-skill` or `gitstow onboard`
141
+ - Auto-updates on version bumps (checks `.version` marker on every CLI invocation)
142
+ - Zero context cost when inactive - only loaded when task matches the skill description
143
+ - Codex runs gitstow CLI commands via Bash - full access to all commands
144
+
145
+ **Optional: MCP server** (`src/gitstow/mcp/server.py`)
146
+ - For non-Codex AI tools (Codex Desktop, Cursor, Windsurf)
147
+ - Install: `pip install gitstow[mcp]`, entry point: `gitstow-mcp`
148
+ - 13 tools + 3 resources, wraps same `core/` modules as CLI
149
+ - **Tradeoff:** MCP tools are always loaded into context (costs tokens even when idle).
150
+ The skill has no such cost. Only use MCP for dedicated repo-management setups.
@@ -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,66 @@ 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
+
38
+ ## [0.2.8] - 2026-07-12
39
+
40
+ ### Fixed
41
+
42
+ - **Pasted GitHub browse URLs now resolve correctly.** URLs like `/tree/...`, `/blob/...`, `/pull/...` now resolve to the repo root instead of being misparsed.
43
+ - **`config migrate-root`** now actually updates workspace paths, and honors the global `-w/--workspace` flag.
44
+ - **`repo freeze/unfreeze/tag/untag`** now honor `-w/--workspace` and report cross-workspace ambiguity clearly instead of guessing.
45
+ - **`pull --json` and `fetch --json`** always emit pure JSON on stdout — no more banners or progress lines interleaved with the payload.
46
+ - **Pull summary** keeps per-workspace identity for same-named frozen repos in different workspaces.
47
+ - **Bulk git operations** no longer hang on credential prompts and are now locale-independent.
48
+ - **`remove --delete`** refuses to delete paths that resolve outside the workspace root.
49
+ - **Workspace labels** are now validated (lowercase alphanumeric, dash, underscore only).
50
+
51
+ ### Added
52
+
53
+ - **Atomic, cross-process-locked writes** to `repos.yaml`, safe for concurrent CLI and web UI use.
54
+ - **Test gate before PyPI publish** — releases now require a passing test suite.
55
+
56
+ ### Security
57
+
58
+ - **Web dashboard CSRF protection.** POST routes now reject cross-origin and DNS-rebinding requests on the localhost UI.
59
+
60
+ ## [0.2.7] - 2026-05-28
61
+
62
+ ### Fixed
63
+
64
+ - **`gitstow onboard`** now uses Beaupy's `default_is_yes` confirmation parameter, fixing first-run setup crashes on confirmation prompts.
65
+ - **AI integration setup** now renders MCP warning text with balanced Rich markup during onboarding and `gitstow setup-ai`.
66
+
7
67
  ## [0.2.5] - 2026-04-19
8
68
 
9
69
  ### Added
@@ -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
@@ -119,6 +120,15 @@ pip install -e ".[tui]" # For TUI development
119
120
  - Repo.global_key (`workspace:key`) for unique identification across workspaces
120
121
  - Legacy format auto-migration (flat repos.yaml → nested, root_path → workspaces)
121
122
 
123
+ ## Product & Implementation Standards
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.
126
+ - Do not recommend quick fixes, temporary patches, or narrow workarounds unless the user explicitly asks for a shortcut.
127
+ - Keep CLI, web dashboard, JSON output, docs, and tests semantically aligned when changing user-facing status behavior.
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.
129
+ - Keep local working-tree state separate from remote relationship state. For example: local changes, clean, ahead, behind, diverged, frozen, missing.
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.
131
+
122
132
  ## AI Integration
123
133
 
124
134
  **Primary: Claude Code skill** (`src/gitstow/skill/SKILL.md`)
@@ -130,6 +140,6 @@ pip install -e ".[tui]" # For TUI development
130
140
  **Optional: MCP server** (`src/gitstow/mcp/server.py`)
131
141
  - For non-Claude-Code AI tools (Claude Desktop, Cursor, Windsurf)
132
142
  - Install: `pip install gitstow[mcp]`, entry point: `gitstow-mcp`
133
- - 13 tools + 3 resources, wraps same `core/` modules as CLI
143
+ - 14 tools + 3 resources, wraps same `core/` modules as CLI
134
144
  - **Tradeoff:** MCP tools are always loaded into context (costs tokens even when idle).
135
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.6
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) |