gitstow 0.1.0__tar.gz → 0.2.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. gitstow-0.2.8/.claude/settings.json +19 -0
  2. gitstow-0.2.8/.github/workflows/publish.yml +84 -0
  3. gitstow-0.2.8/AGENTS.md +150 -0
  4. {gitstow-0.1.0 → gitstow-0.2.8}/BACKLOG.md +3 -1
  5. gitstow-0.2.8/CHANGELOG.md +141 -0
  6. {gitstow-0.1.0 → gitstow-0.2.8}/CLAUDE.md +19 -4
  7. {gitstow-0.1.0 → gitstow-0.2.8}/CONTRIBUTING.md +12 -0
  8. {gitstow-0.1.0 → gitstow-0.2.8}/PKG-INFO +36 -6
  9. {gitstow-0.1.0 → gitstow-0.2.8}/README.md +30 -5
  10. gitstow-0.2.8/demo.gif +0 -0
  11. {gitstow-0.1.0 → gitstow-0.2.8}/demo.tape +15 -6
  12. gitstow-0.2.8/docs/building/audit-2026-07-06.md +149 -0
  13. {gitstow-0.1.0 → gitstow-0.2.8}/docs/building/implementation-plan.md +14 -2
  14. gitstow-0.2.8/docs/building/plans/2026-07-06-wave-1-correctness-safety.md +1301 -0
  15. gitstow-0.2.8/docs/building/plans/2026-07-06-wave-2-status-model.md +1097 -0
  16. gitstow-0.2.8/docs/building/plans/2026-07-06-wave-3-structure-polish.md +1690 -0
  17. {gitstow-0.1.0 → gitstow-0.2.8}/docs/user/commands.md +131 -0
  18. {gitstow-0.1.0 → gitstow-0.2.8}/docs/user/concepts.md +1 -0
  19. {gitstow-0.1.0 → gitstow-0.2.8}/docs/user/getting-started.md +16 -1
  20. {gitstow-0.1.0 → gitstow-0.2.8}/pyproject.toml +6 -1
  21. gitstow-0.2.8/scripts/release.sh +137 -0
  22. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/__init__.py +1 -1
  23. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/config_cmd.py +39 -38
  24. gitstow-0.2.8/src/gitstow/cli/fetch.py +225 -0
  25. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/main.py +7 -0
  26. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/manage.py +38 -33
  27. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/onboard.py +16 -7
  28. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/pull.py +12 -11
  29. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/remove.py +11 -0
  30. gitstow-0.2.8/src/gitstow/cli/serve.py +49 -0
  31. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/setup_ai.py +4 -4
  32. gitstow-0.2.8/src/gitstow/cli/update.py +146 -0
  33. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/workspace_cmd.py +15 -0
  34. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/core/git.py +40 -1
  35. gitstow-0.2.8/src/gitstow/core/locking.py +63 -0
  36. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/core/repo.py +53 -34
  37. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/core/url_parser.py +33 -10
  38. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/skill/SKILL.md +13 -0
  39. gitstow-0.2.8/src/gitstow/web/__init__.py +4 -0
  40. gitstow-0.2.8/src/gitstow/web/routes/__init__.py +1 -0
  41. gitstow-0.2.8/src/gitstow/web/routes/collection.py +245 -0
  42. gitstow-0.2.8/src/gitstow/web/routes/dashboard.py +246 -0
  43. gitstow-0.2.8/src/gitstow/web/routes/pages.py +91 -0
  44. gitstow-0.2.8/src/gitstow/web/routes/repos.py +489 -0
  45. gitstow-0.2.8/src/gitstow/web/routes/system.py +23 -0
  46. gitstow-0.2.8/src/gitstow/web/routes/workspaces.py +121 -0
  47. gitstow-0.2.8/src/gitstow/web/server.py +129 -0
  48. gitstow-0.2.8/src/gitstow/web/static/app.css +922 -0
  49. gitstow-0.2.8/src/gitstow/web/templates/_repo_drawer.html +111 -0
  50. gitstow-0.2.8/src/gitstow/web/templates/add_repo.html +52 -0
  51. gitstow-0.2.8/src/gitstow/web/templates/base.html +132 -0
  52. gitstow-0.2.8/src/gitstow/web/templates/dashboard.html +196 -0
  53. gitstow-0.2.8/src/gitstow/web/templates/partials/dashboard_rows.html +1 -0
  54. gitstow-0.2.8/src/gitstow/web/templates/partials/fetch_summary.html +39 -0
  55. gitstow-0.2.8/src/gitstow/web/templates/partials/pull_summary.html +39 -0
  56. gitstow-0.2.8/src/gitstow/web/templates/partials/repo_row.html +76 -0
  57. gitstow-0.2.8/src/gitstow/web/templates/settings.html +103 -0
  58. gitstow-0.2.8/src/gitstow/web/templates/workspaces.html +97 -0
  59. gitstow-0.2.8/tests/test_cli.py +266 -0
  60. {gitstow-0.1.0 → gitstow-0.2.8}/tests/test_config.py +63 -0
  61. {gitstow-0.1.0 → gitstow-0.2.8}/tests/test_git.py +54 -0
  62. gitstow-0.2.8/tests/test_locking.py +64 -0
  63. gitstow-0.2.8/tests/test_onboard.py +122 -0
  64. {gitstow-0.1.0 → gitstow-0.2.8}/tests/test_repo.py +2 -2
  65. gitstow-0.2.8/tests/test_serve.py +435 -0
  66. gitstow-0.2.8/tests/test_setup_ai.py +49 -0
  67. {gitstow-0.1.0 → gitstow-0.2.8}/tests/test_url_parser.py +35 -0
  68. gitstow-0.1.0/.github/workflows/release.yml +0 -45
  69. gitstow-0.1.0/CHANGELOG.md +0 -27
  70. gitstow-0.1.0/tests/test_cli.py +0 -62
  71. {gitstow-0.1.0 → gitstow-0.2.8}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  72. {gitstow-0.1.0 → gitstow-0.2.8}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  73. {gitstow-0.1.0 → gitstow-0.2.8}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  74. {gitstow-0.1.0 → gitstow-0.2.8}/.github/workflows/ci.yml +0 -0
  75. {gitstow-0.1.0 → gitstow-0.2.8}/.gitignore +0 -0
  76. {gitstow-0.1.0 → gitstow-0.2.8}/CODE_OF_CONDUCT.md +0 -0
  77. {gitstow-0.1.0 → gitstow-0.2.8}/LICENSE +0 -0
  78. {gitstow-0.1.0 → gitstow-0.2.8}/SECURITY.md +0 -0
  79. {gitstow-0.1.0 → gitstow-0.2.8}/docs/user/configuration.md +0 -0
  80. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/__main__.py +0 -0
  81. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/__init__.py +0 -0
  82. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/add.py +0 -0
  83. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/doctor.py +0 -0
  84. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/exec_cmd.py +0 -0
  85. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/export_cmd.py +0 -0
  86. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/helpers.py +0 -0
  87. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/list_cmd.py +0 -0
  88. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/migrate.py +0 -0
  89. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/open_cmd.py +0 -0
  90. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/search.py +0 -0
  91. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/shell.py +0 -0
  92. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/skill_cmd.py +0 -0
  93. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/stats.py +0 -0
  94. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/status.py +0 -0
  95. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/cli/tui.py +0 -0
  96. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/core/__init__.py +0 -0
  97. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/core/config.py +0 -0
  98. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/core/discovery.py +0 -0
  99. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/core/parallel.py +0 -0
  100. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/core/paths.py +0 -0
  101. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/mcp/__init__.py +0 -0
  102. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/mcp/server.py +0 -0
  103. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/tui/__init__.py +0 -0
  104. {gitstow-0.1.0 → gitstow-0.2.8}/src/gitstow/tui/app.py +0 -0
  105. {gitstow-0.1.0 → gitstow-0.2.8}/tests/__init__.py +0 -0
  106. {gitstow-0.1.0 → gitstow-0.2.8}/tests/conftest.py +0 -0
@@ -0,0 +1,19 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(python -m pytest tests/test_serve.py -v)",
5
+ "Bash(python -c \"from gitstow.cli.main import app; from typer.testing import CliRunner; r = CliRunner\\(\\); print\\(r.invoke\\(app, ['ui', '--help']\\).output\\)\")",
6
+ "Bash(python3 -m pytest tests/test_serve.py -v)",
7
+ "Bash(python3 -c \"from gitstow.cli.main import app; from typer.testing import CliRunner; r = CliRunner\\(\\); print\\(r.invoke\\(app, ['ui', '--help']\\).output\\)\")",
8
+ "Bash(python3 -c \"from gitstow.cli.main import app; from typer.testing import CliRunner; r = CliRunner\\(\\); print\\(r.invoke\\(app, ['serve', '--help']\\).output\\)\")",
9
+ "Bash(python3 -m pytest -v)",
10
+ "Bash(git commit -m ':*)",
11
+ "Bash(python3 -m pytest tests/test_git.py tests/test_serve.py -v)",
12
+ "Bash(python3 -c \"from gitstow.cli.main import app; from typer.testing import CliRunner; r = CliRunner\\(\\); print\\(r.invoke\\(app, ['fetch', '--help']\\).output\\)\")",
13
+ "Bash(git tag:*)",
14
+ "Bash(python3 -c \"import gitstow; print\\(gitstow.__version__\\)\")",
15
+ "Bash(pip3 show:*)",
16
+ "Bash(python3 -m pytest tests/test_serve.py::TestFetch -v)"
17
+ ]
18
+ }
19
+ }
@@ -0,0 +1,84 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
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
+
26
+ publish:
27
+ needs: test
28
+ runs-on: ubuntu-latest
29
+ environment: pypi
30
+ permissions:
31
+ contents: read
32
+ id-token: write
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+
36
+ - uses: actions/setup-python@v5
37
+ with:
38
+ python-version: "3.12"
39
+
40
+ - name: Install build tools
41
+ run: pip install build
42
+
43
+ - name: Build package
44
+ run: python -m build
45
+
46
+ - name: Verify version matches tag
47
+ run: |
48
+ python - <<'PY'
49
+ import os
50
+ import pathlib
51
+ import re
52
+ import sys
53
+ import tomllib
54
+
55
+ tag_version = os.environ["GITHUB_REF"].removeprefix("refs/tags/v")
56
+
57
+ with open("pyproject.toml", "rb") as f:
58
+ package_version = tomllib.load(f)["project"]["version"]
59
+
60
+ init_text = pathlib.Path("src/gitstow/__init__.py").read_text()
61
+ match = re.search(r'__version__ = "([^"]+)"', init_text)
62
+ init_version = match.group(1) if match else None
63
+
64
+ errors = []
65
+ if tag_version != package_version:
66
+ errors.append(
67
+ f"Tag v{tag_version} does not match package version {package_version}"
68
+ )
69
+ if init_version != package_version:
70
+ errors.append(
71
+ f"src/gitstow/__init__.py version {init_version} "
72
+ f"does not match package version {package_version}"
73
+ )
74
+
75
+ if errors:
76
+ for error in errors:
77
+ print(f"::error::{error}")
78
+ sys.exit(1)
79
+
80
+ print(f"Publishing gitstow v{package_version}")
81
+ PY
82
+
83
+ - name: Publish to PyPI
84
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -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.
@@ -10,7 +10,9 @@ Post-v0.1.0 improvements tracked here. See [GitHub Issues](https://github.com/ri
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
12
  - [x] **Publish to PyPI** (#6) — Release workflow created (`.github/workflows/release.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
+ - [ ] **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.
13
15
 
14
16
  ## Documentation
15
17
 
16
- - [ ] **Record demo GIF for README** (#7) — VHS tape script created (`demo.tape`); run `vhs demo.tape` to record
18
+ - [x] **Record demo GIF for README** (#7) — Recorded with VHS, embedded in README
@@ -0,0 +1,141 @@
1
+ # Changelog
2
+
3
+ All notable changes to gitstow will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.2.8] - 2026-07-12
8
+
9
+ ### Fixed
10
+
11
+ - **Pasted GitHub browse URLs now resolve correctly.** URLs like `/tree/...`, `/blob/...`, `/pull/...` now resolve to the repo root instead of being misparsed.
12
+ - **`config migrate-root`** now actually updates workspace paths, and honors the global `-w/--workspace` flag.
13
+ - **`repo freeze/unfreeze/tag/untag`** now honor `-w/--workspace` and report cross-workspace ambiguity clearly instead of guessing.
14
+ - **`pull --json` and `fetch --json`** always emit pure JSON on stdout — no more banners or progress lines interleaved with the payload.
15
+ - **Pull summary** keeps per-workspace identity for same-named frozen repos in different workspaces.
16
+ - **Bulk git operations** no longer hang on credential prompts and are now locale-independent.
17
+ - **`remove --delete`** refuses to delete paths that resolve outside the workspace root.
18
+ - **Workspace labels** are now validated (lowercase alphanumeric, dash, underscore only).
19
+
20
+ ### Added
21
+
22
+ - **Atomic, cross-process-locked writes** to `repos.yaml`, safe for concurrent CLI and web UI use.
23
+ - **Test gate before PyPI publish** — releases now require a passing test suite.
24
+
25
+ ### Security
26
+
27
+ - **Web dashboard CSRF protection.** POST routes now reject cross-origin and DNS-rebinding requests on the localhost UI.
28
+
29
+ ## [0.2.7] - 2026-05-28
30
+
31
+ ### Fixed
32
+
33
+ - **`gitstow onboard`** now uses Beaupy's `default_is_yes` confirmation parameter, fixing first-run setup crashes on confirmation prompts.
34
+ - **AI integration setup** now renders MCP warning text with balanced Rich markup during onboarding and `gitstow setup-ai`.
35
+
36
+ ## [0.2.5] - 2026-04-19
37
+
38
+ ### Added
39
+
40
+ - **`gitstow fetch`** — new CLI command to fetch all remotes without merging. Updates ahead/behind counts. Includes frozen repos (fetch is non-destructive). Supports `--tag`, `--exclude-tag`, `--owner`, `--retry`, `--json`, `--quiet` flags.
41
+ - **Web dashboard — Fetch all button.** New `btn-outline` button in the action bar between Refresh and Add repo. Fetches all remotes in parallel (including frozen repos), then shows a summary panel. No confirmation needed — fetch is non-destructive.
42
+ - **Web dashboard — single-repo Fetch.** New "Fetch" action in each row's three-dot menu. Updates that row's ahead/behind counts in place via HTMX.
43
+ - **`last_fetched` timestamp** tracked per repo in `repos.yaml`. Stamped on successful fetch operations. Backward-compatible — existing repos default to empty.
44
+ - Updated help dialog and tooltips to reference Fetch all instead of "fetch manually."
45
+
46
+ ## [0.2.4] - 2026-04-19
47
+
48
+ ### Changed
49
+
50
+ - **Renamed `gitstow serve` → `gitstow ui`** — more intuitive command name for the browser dashboard. `gitstow serve` remains as a hidden alias for backwards compatibility.
51
+ - **Removed `WEB_GUI_PLAN.md`** — shipped planning artifact retired; git history preserves it.
52
+
53
+ ## [0.2.3] - 2026-04-18
54
+
55
+ ### Fixed
56
+
57
+ - **Web dashboard — "Open folder" now actually opens the folder.** The row-menu and drawer buttons were previously no-ops; they now POST to a new `/repos/{ws}/{key}/open-folder` route which shells out to the platform opener (`open` on macOS, `xdg-open` on Linux, `explorer` on Windows). The server binds to `127.0.0.1` only, so no exposure beyond localhost.
58
+
59
+ ### Added
60
+
61
+ - **Web dashboard — Copy URL / Copy path / Copy local path actions.** The `⋯` row menu and the repo drawer each expose three clipboard actions: Copy URL (remote clone URL), Copy path (repo key like `owner/repo`), Copy local path (absolute filesystem path). Implemented client-side with `navigator.clipboard` and a textarea fallback for non-secure contexts.
62
+ - **Toast notifications.** A new bottom-center toast surfaces success/failure for Open folder and the copy actions (reads an optional `data-toast` attr on any `hx-post` button and an error message from the JSON response body on failure).
63
+
64
+ ## [0.2.2] - 2026-04-18
65
+
66
+ ### Added
67
+
68
+ - **Dashboard tooltips everywhere.** Every interactive element now has a `title=` explaining what it does or what its state means — status pills (with the recommended next step), delta badges, workspace chips, tags, last-pull, Pull button (text varies by repo state), the `⋯` menu and each of its items, action-bar buttons, column headers, the nav links, the live dot, the Shutdown button, the lock icon. Hover over anything.
69
+ - **Pull button now shows the commit count when behind.** `↓ Pull 5` instead of just `Pull` — the action's payload is visible at a glance without hover.
70
+ - **"Reading the dashboard" help dialog.** New `?` button in the hero opens a native `<dialog>` modal with: statuses + what each means + what to do, the Pull button color convention, what Remote Δ reflects (last fetch, not live remote), what auto-refresh actually does (local state only; does NOT run `git fetch`), and a reference for every action. Click the backdrop or press Esc to close.
71
+ - Matching `docs/user/commands.md` "Reading the dashboard" subsection under `gitstow ui` so the legend exists in prose too, not only in the UI.
72
+
73
+ ### Changed
74
+
75
+ - `_classify` and `_delta` helpers in `web/routes/dashboard.py` now also return tooltip strings; `_pull_tooltip` maps (variant × status) to the exact explanation shown on each Pull button. Partial template `partials/repo_row.html` threads these through consistently so HTMX row swaps after pull keep the tooltips accurate.
76
+
77
+ ## [0.2.1] - 2026-04-17
78
+
79
+ ### Added
80
+
81
+ - **`gitstow update`** — self-upgrade from PyPI. Detects the install method (pipx, pip, or editable) and runs the matching upgrade command. Use `--check` / `-c` to query PyPI without installing — shows newer-version-available or up-to-date. Editable installs get a friendly note pointing at `git pull` instead of trying to pip-upgrade.
82
+ - Smoke tests for `serve` and `update` command help.
83
+
84
+ ### Fixed
85
+
86
+ - **CI**: added `httpx` to `[dev]` extras. `fastapi.testclient.TestClient` re-exports Starlette's version, which imports `httpx` at module load. Fresh CI installs were failing at test collection; local envs passed because `httpx` was already present from other packages.
87
+
88
+ ## [0.2.0] - 2026-04-17
89
+
90
+ ### Added
91
+
92
+ - **`gitstow ui`** — a persistent local browser dashboard for daily repo management. Launches a FastAPI + Jinja2 + HTMX server at `http://127.0.0.1:7853` and auto-opens your default browser. Dark theme (Bricolage Grotesque display, JetBrains Mono data) with ember-orange primary accent and signal-blue secondary.
93
+ - **Dashboard** — ledger view of every tracked repo: colored-dot status (clean / dirty / conflict / behind / ahead / frozen), workspace chip, branch, remote delta, tags, last-pull time. Hero metrics strip summarizes counts. Hover reveals an accent indicator line on the row's left edge.
94
+ - **Pull** — single-repo pull with HTMX row swap in place; **Pull all** runs every non-frozen repo in parallel via the existing `core.parallel` semaphore and renders a summary panel (`N ok · N failed · N skipped`) with per-repo failure details.
95
+ - **Add repo** — form with URL parser, workspace selector, and optional tags; merges workspace auto-tags on clone.
96
+ - **Remove** — registry-only from the row's ⋯ menu (HTMX row-delete); registry + disk from the repo drawer (defensive path check; refuses to rmtree outside the workspace root).
97
+ - **Freeze / tag toggles** — row-menu freeze with HTMX label flip; drawer freeze checkbox posts on change; drawer tag editor replaces the list from a comma-separated input.
98
+ - **Workspaces** — list, add (label + path + layout + auto-tags), remove (files untouched), **Scan** (discovers untracked repos on disk and catalogs them with auto-tags merged).
99
+ - **Collection export / import** — under Settings; downloads YAML (canonical round-trip), JSON, or plain URL list; upload accepts any of the three.
100
+ - **Auto-refresh** — dashboard tbody reloads every 30s via HTMX `hx-trigger="every 30s"` without touching hero, metrics, or action bar.
101
+ - **Shutdown** — footer button POSTs `/shutdown`, which flips `uvicorn.Server.should_exit` (Ctrl+C also works).
102
+ - **New core dependencies** (shipped, not behind an extra): `fastapi>=0.110`, `uvicorn>=0.27`, `jinja2>=3.1`, `python-multipart>=0.0.9`.
103
+ - **30 FastAPI TestClient smoke tests** covering every route + mutation path. Monkeypatches isolate config/repo files to tmp; git calls are mocked (never shells out).
104
+
105
+ ### Changed
106
+
107
+ - Command count 29 → 30 (`serve` added).
108
+ - `CLAUDE.md` architecture tree expanded to document the new `web/` module.
109
+
110
+ ### Security
111
+
112
+ - `gitstow ui` binds `127.0.0.1` only. There is no `--host` flag — arbitrary git execution must not be LAN-reachable.
113
+ - All mutations are POST; `/shutdown` is POST so stray links can't terminate the server.
114
+ - Registry + disk delete resolves the repo path and verifies it lives under the workspace root before `rmtree`.
115
+
116
+ ### Deferred
117
+
118
+ - The Textual TUI has multiple breakages and is parked in [BACKLOG.md](BACKLOG.md); `gitstow ui` is the primary visual dashboard now. TUI remains for the eventual SSH / remote use case.
119
+ - Web GUI is intentionally single-user + localhost-only. Auth, multi-user, HTTPS, remote access, and daemonization are explicitly out of scope for v0.2.
120
+
121
+ ## [0.1.0] - 2026-04-10
122
+
123
+ Initial release.
124
+
125
+ ### Added
126
+
127
+ - **Core commands:** `add`, `pull`, `list`, `status`, `remove`, `migrate`
128
+ - **Workspace system:** Multiple workspaces with structured (`owner/repo`) and flat (`repo`) layouts
129
+ - **Workspace commands:** `workspace list`, `workspace add`, `workspace remove`, `workspace scan`
130
+ - **Repo management:** `repo freeze`, `repo unfreeze`, `repo tag`, `repo untag`, `repo tags`, `repo info`
131
+ - **Bulk operations:** Parallel pull/status with configurable concurrency (default 6)
132
+ - **Power commands:** `exec` (run commands across repos), `search` (grep across repos via ripgrep), `open` (editor/browser/finder), `stats`
133
+ - **Collection sharing:** `collection export` (YAML/JSON/URLs) and `collection import`
134
+ - **Shell integration:** `shell pick` (fzf picker), `shell init` (aliases), `shell setup`
135
+ - **Interactive TUI:** `tui` command with Textual-based dashboard (filter, pull, freeze toggle)
136
+ - **URL parsing:** GitHub, GitLab, Bitbucket, Codeberg, Azure DevOps, custom hosts; HTTPS, SSH, and shorthand formats
137
+ - **AI integration:** Claude Code skill (auto-installed via `onboard` or `install-skill`) and optional MCP server
138
+ - **Setup:** `onboard` wizard, `doctor` health check, `config show/set`
139
+ - **Output modes:** `--json` and `--quiet` flags on all main commands
140
+ - **Global workspace filter:** `-w/--workspace` flag on all commands
141
+ - **Error isolation:** One failing repo never blocks operations on others
@@ -26,6 +26,7 @@ src/gitstow/
26
26
  │ ├── export_cmd.py # export/import collection
27
27
  │ ├── shell.py # Shell integration (fzf, aliases)
28
28
  │ ├── tui.py # TUI launcher
29
+ │ ├── serve.py # Web dashboard launcher
29
30
  │ ├── migrate.py, config_cmd.py, onboard.py, doctor.py, skill_cmd.py
30
31
  │ └── __init__.py
31
32
  ├── core/ # Business logic (git ops, URL parsing, state)
@@ -40,6 +41,11 @@ src/gitstow/
40
41
  ├── tui/ # Textual interactive dashboard
41
42
  │ ├── app.py # Main TUI application
42
43
  │ └── __init__.py
44
+ ├── web/ # FastAPI browser dashboard (gitstow ui)
45
+ │ ├── server.py # FastAPI app, uvicorn runner, app.state.server stash
46
+ │ ├── static/app.css # Dark theme, Bricolage Grotesque + JetBrains Mono
47
+ │ ├── templates/ # Jinja2 — base.html + page templates + partials/
48
+ │ └── routes/ # dashboard.py, repos.py, workspaces.py, collection.py, pages.py, system.py
43
49
  └── skill/ # Claude Code skill (SKILL.md)
44
50
  └── SKILL.md
45
51
  ```
@@ -80,15 +86,15 @@ active:
80
86
  tags: [active]
81
87
  ```
82
88
 
83
- ## All Commands (26)
89
+ ## All Commands (32)
84
90
 
85
- **Core:** `add`, `pull`, `list`, `status`, `remove`, `migrate`
91
+ **Core:** `add`, `pull`, `fetch`, `list`, `status`, `remove`, `migrate`
86
92
  **Workspace:** `workspace list`, `workspace add`, `workspace remove`, `workspace scan`
87
93
  **Repo management:** `repo freeze`, `repo unfreeze`, `repo tag`, `repo untag`, `repo tags`, `repo info`
88
94
  **Power:** `exec`, `search`, `open`, `stats`
89
95
  **Sharing:** `collection export`, `collection import`
90
- **Shell:** `shell pick`, `shell init`, `shell setup`, `tui`
91
- **Setup:** `onboard`, `config show/set/path/migrate-root`, `doctor`, `install-skill`
96
+ **Shell:** `shell pick`, `shell init`, `shell completions`, `shell setup`, `tui`, `ui`
97
+ **Setup:** `onboard`, `config show/set/path/migrate-root`, `doctor`, `install-skill`, `setup-ai`, `update`
92
98
 
93
99
  ## Development
94
100
 
@@ -113,6 +119,15 @@ pip install -e ".[tui]" # For TUI development
113
119
  - Repo.global_key (`workspace:key`) for unique identification across workspaces
114
120
  - Legacy format auto-migration (flat repos.yaml → nested, root_path → workspaces)
115
121
 
122
+ ## Product & Implementation Standards
123
+
124
+ - 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
+ - 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
+ - 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
+ - Keep local working-tree state separate from remote relationship state. For example: local changes, clean, ahead, behind, diverged, frozen, missing.
129
+ - 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.
130
+
116
131
  ## AI Integration
117
132
 
118
133
  **Primary: Claude Code skill** (`src/gitstow/skill/SKILL.md`)
@@ -40,6 +40,7 @@ The codebase has a strict layered architecture:
40
40
  cli/ → Thin command layer (Typer). Delegates to core/.
41
41
  core/ → Business logic. Git ops, config, repo store, URL parsing.
42
42
  tui/ → Textual interactive dashboard.
43
+ web/ → FastAPI + Jinja2 + HTMX browser dashboard (`gitstow ui`).
43
44
  skill/ → Claude Code skill (SKILL.md).
44
45
  mcp/ → MCP server for non-Claude-Code AI tools.
45
46
  ```
@@ -67,6 +68,17 @@ See `CLAUDE.md` for a detailed architecture guide.
67
68
  - The Claude Code skill auto-updates when the version changes (checked on every CLI invocation)
68
69
  - Follow [Semantic Versioning](https://semver.org/): breaking.feature.fix
69
70
 
71
+ Release from a `main` branch with no tracked changes:
72
+
73
+ ```bash
74
+ bash scripts/release.sh 0.2.6 "short release note"
75
+ ```
76
+
77
+ The release script updates both version files, commits the bump, creates `vX.Y.Z`,
78
+ and pushes the commit plus tag. The `Publish to PyPI` workflow runs on pushed `v*`
79
+ tags and verifies the tag, `pyproject.toml`, and `src/gitstow/__init__.py` versions
80
+ match before publishing.
81
+
70
82
  ## What to Work On
71
83
 
72
84
  - Check [open issues](https://github.com/rishmadaan/gitstow/issues) for things to pick up
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitstow
3
- Version: 0.1.0
3
+ Version: 0.2.8
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,10 +26,15 @@ 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
29
32
  Requires-Dist: pyyaml>=6.0
30
33
  Requires-Dist: rich>=13.0
31
34
  Requires-Dist: typer[all]>=0.9
35
+ Requires-Dist: uvicorn>=0.27
32
36
  Provides-Extra: dev
37
+ Requires-Dist: httpx>=0.27; extra == 'dev'
33
38
  Requires-Dist: pytest>=8.0; extra == 'dev'
34
39
  Requires-Dist: ruff>=0.4; extra == 'dev'
35
40
  Provides-Extra: mcp
@@ -42,6 +47,8 @@ Description-Content-Type: text/markdown
42
47
 
43
48
  A git repository library manager — clone, organize, and maintain collections of repos you learn from.
44
49
 
50
+ ![gitstow demo](demo.gif)
51
+
45
52
  ## Why?
46
53
 
47
54
  AI-assisted development has created a new relationship with open source. Developers increasingly maintain local clones of repositories they study and reference, not just repos they contribute to. `gitstow` manages this collection as a first-class concern.
@@ -70,11 +77,10 @@ gitstow pull
70
77
 
71
78
  # See your collection
72
79
  gitstow list
73
-
74
- # Check git status across all repos
75
- gitstow status
76
80
  ```
77
81
 
82
+ Prefer a GUI? Run `gitstow ui` — a local dark-themed browser dashboard opens automatically. Dirty state, branch deltas, Pull / Pull-all, add / remove / freeze / tag, workspace CRUD, collection export/import — all in one tab you leave open. See [Browser Dashboard](#browser-dashboard).
83
+
78
84
  ## Features
79
85
 
80
86
  ### Workspaces
@@ -174,9 +180,17 @@ gse # Open repo in editor (fzf picker)
174
180
  gsp # gitstow pull shorthand
175
181
  ```
176
182
 
177
- ### Interactive Dashboard
183
+ ### Browser Dashboard
184
+ ```bash
185
+ gitstow ui # opens http://127.0.0.1:7853 in your browser
186
+ gitstow ui --port 8080
187
+ gitstow ui --no-browser
188
+ ```
189
+ 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
+
191
+ ### Interactive Dashboard (TUI)
178
192
  ```bash
179
- gitstow tui # Keyboard-driven dashboard with filter, pull, freeze
193
+ gitstow tui # Keyboard-driven terminal dashboard (SSH-friendly)
180
194
  ```
181
195
 
182
196
  ### JSON Output
@@ -220,14 +234,21 @@ gitstow status --json
220
234
  | `gitstow collection import <file>` | Import a collection from file |
221
235
  | **Shell** | |
222
236
  | `gitstow shell setup` | Show shell integration instructions |
237
+ | `gitstow shell init` | Print shell functions to source in rc file |
223
238
  | `gitstow shell pick` | fzf-powered repo picker |
239
+ | `gitstow shell completions` | Tab completion for repo names, workspaces, tags |
224
240
  | `gitstow tui` | Interactive terminal dashboard |
241
+ | `gitstow fetch` | Fetch all remotes — update ahead/behind counts without merging |
242
+ | `gitstow ui` | Launch local browser dashboard (http://127.0.0.1:7853) |
243
+ | `gitstow update` | Upgrade gitstow itself from PyPI (detects pipx / pip / editable) |
244
+ | `gitstow update --check` | Check PyPI for a newer version without installing |
225
245
  | **Config** | |
226
246
  | `gitstow config show` | Show current config and workspaces |
227
247
  | `gitstow config set <key> <value>` | Change a setting |
228
248
  | `gitstow onboard` | First-run setup wizard |
229
249
  | `gitstow doctor` | Health check |
230
250
  | `gitstow install-skill` | Install Claude Code skill |
251
+ | `gitstow setup-ai` | Auto-detect AI tools and configure integration |
231
252
 
232
253
  All commands accept `-w <label>` to filter to a specific workspace.
233
254
 
@@ -299,6 +320,15 @@ pytest
299
320
  ruff check src/
300
321
  ```
301
322
 
323
+ Release from a `main` branch with no tracked changes:
324
+
325
+ ```bash
326
+ bash scripts/release.sh 0.2.6 "short release note"
327
+ ```
328
+
329
+ The script bumps `pyproject.toml` and `src/gitstow/__init__.py`, commits the bump,
330
+ creates `vX.Y.Z`, and pushes the commit plus tag. Pushing the tag publishes to PyPI.
331
+
302
332
  See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide.
303
333
 
304
334
  ## License