arc-prs 0.1.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.
- arc_prs-0.1.0/.github/workflows/ci.yml +29 -0
- arc_prs-0.1.0/.github/workflows/release.yml +55 -0
- arc_prs-0.1.0/.gitignore +30 -0
- arc_prs-0.1.0/PKG-INFO +7 -0
- arc_prs-0.1.0/README.md +252 -0
- arc_prs-0.1.0/arc/__init__.py +0 -0
- arc_prs-0.1.0/arc/cli.py +623 -0
- arc_prs-0.1.0/arc/git.py +119 -0
- arc_prs-0.1.0/arc/github.py +60 -0
- arc_prs-0.1.0/arc/ops.py +117 -0
- arc_prs-0.1.0/arc/state.py +86 -0
- arc_prs-0.1.0/pyproject.toml +22 -0
- arc_prs-0.1.0/skills/arc.md +214 -0
- arc_prs-0.1.0/tests/__init__.py +0 -0
- arc_prs-0.1.0/tests/conftest.py +16 -0
- arc_prs-0.1.0/tests/test_cli.py +714 -0
- arc_prs-0.1.0/tests/test_git.py +98 -0
- arc_prs-0.1.0/tests/test_github.py +87 -0
- arc_prs-0.1.0/tests/test_ops.py +181 -0
- arc_prs-0.1.0/tests/test_state.py +113 -0
- arc_prs-0.1.0/uv.lock +309 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- uses: astral-sh/setup-uv@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: uv run pytest --tb=short -q
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version:
|
|
7
|
+
description: "Version to release (e.g. 0.1.0) — must match version in pyproject.toml"
|
|
8
|
+
required: true
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
release:
|
|
12
|
+
name: Build, tag, and publish
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: release
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write # create tags and GitHub releases
|
|
17
|
+
id-token: write # PyPI trusted publishing (no API token needed)
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- uses: astral-sh/setup-uv@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.11"
|
|
25
|
+
|
|
26
|
+
- name: Validate version matches pyproject.toml
|
|
27
|
+
run: |
|
|
28
|
+
ACTUAL=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
29
|
+
if [ "$ACTUAL" != "${{ inputs.version }}" ]; then
|
|
30
|
+
echo "Version mismatch: pyproject.toml has $ACTUAL but release input is ${{ inputs.version }}"
|
|
31
|
+
echo "Bump the version in pyproject.toml via a PR first, then re-run this workflow."
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
- name: Build
|
|
36
|
+
run: uv build
|
|
37
|
+
|
|
38
|
+
- name: Create git tag
|
|
39
|
+
run: |
|
|
40
|
+
git config user.name "github-actions[bot]"
|
|
41
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
42
|
+
git tag -a "v${{ inputs.version }}" -m "Release v${{ inputs.version }}"
|
|
43
|
+
git push origin "v${{ inputs.version }}"
|
|
44
|
+
|
|
45
|
+
- name: Create GitHub Release
|
|
46
|
+
run: |
|
|
47
|
+
gh release create "v${{ inputs.version }}" \
|
|
48
|
+
dist/* \
|
|
49
|
+
--title "v${{ inputs.version }}" \
|
|
50
|
+
--generate-notes
|
|
51
|
+
env:
|
|
52
|
+
GH_TOKEN: ${{ github.token }}
|
|
53
|
+
|
|
54
|
+
- name: Publish to PyPI
|
|
55
|
+
run: uv publish
|
arc_prs-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Distribution / packaging
|
|
8
|
+
*.egg-info/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg
|
|
12
|
+
|
|
13
|
+
# Virtual environments
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
|
|
18
|
+
# Testing
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# uv lockfile (commit this if you want reproducible installs across machines)
|
|
24
|
+
# uv.lock
|
|
25
|
+
|
|
26
|
+
# arc stack state — per-clone, not shared
|
|
27
|
+
.arc/state.json
|
|
28
|
+
|
|
29
|
+
# Internal design docs — not for public repo
|
|
30
|
+
docs/superpowers/
|
arc_prs-0.1.0/PKG-INFO
ADDED
arc_prs-0.1.0/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# arc
|
|
2
|
+
|
|
3
|
+
Your PR has 47 files changed. Nobody's going to review that.
|
|
4
|
+
|
|
5
|
+
Stacked PRs fix this — break a large change into a chain of small, focused diffs that reviewers can actually follow. The problem is that managing a stack by hand is painful. Every time `main` moves, you cascade rebases through four branches manually. Every merged PR means retargeting the one above it. So you skip the stacking and ship the monster PR anyway.
|
|
6
|
+
|
|
7
|
+
`arc` removes that friction.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
$ arc status
|
|
11
|
+
|
|
12
|
+
main
|
|
13
|
+
└── feat/auth PR #42 ✓ 2 commits (rev 3)
|
|
14
|
+
└── feat/api PR #43 ✗ 3 commits (rev 1) ← needs rebase
|
|
15
|
+
└── feat/ui no PR ✓ 1 commit
|
|
16
|
+
|
|
17
|
+
→ Run 'arc sync' to rebase feat/api onto feat/auth.
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
One command keeps the whole stack current. Another opens all the PRs — with a stack map in each description so reviewers can navigate. When a PR merges, `arc land` rebases everything above it and removes it from the stack.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pipx install arc-stack
|
|
28
|
+
# or
|
|
29
|
+
uv tool install arc-stack
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Requires:** Python 3.11+, [git](https://git-scm.com), [gh CLI](https://cli.github.com) (authenticated via `gh auth login`)
|
|
33
|
+
|
|
34
|
+
First time on a new machine:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
arc setup # checks git, gh auth, and configures git rerere
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Building a stack
|
|
43
|
+
|
|
44
|
+
Initialize `arc` in your repo once:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
arc init --base main --prefix feat
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This creates `.arc/state.json` (git-ignored, per-clone) and adds it to `.gitignore`. The `--prefix` is optional — if set, `arc new auth` creates `feat/auth` instead of `auth`.
|
|
51
|
+
|
|
52
|
+
Then build your stack branch by branch as you work:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
arc new auth
|
|
56
|
+
# write code, run tests
|
|
57
|
+
git add . && git commit -m "Add auth middleware"
|
|
58
|
+
|
|
59
|
+
arc new api
|
|
60
|
+
# write code
|
|
61
|
+
git add . && git commit -m "Add API routes"
|
|
62
|
+
|
|
63
|
+
arc new ui
|
|
64
|
+
git add . && git commit -m "Add frontend"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Each `arc new` creates a branch from your current HEAD and registers it in the stack. `arc status` shows you where you are at any point.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## The daily loop
|
|
72
|
+
|
|
73
|
+
When `main` moves or you amend a lower branch, run:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
arc sync
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This fetches the latest from remote and cascades a rebase bottom-up through the stack — `feat/auth` onto `main`, `feat/api` onto `feat/auth`, `feat/ui` onto `feat/api`. If there's a conflict, `arc` aborts the rebase, resets every branch to where it was before, and tells you exactly which files to fix:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Conflict in feat/api. Resolve: src/api.py
|
|
83
|
+
Then run 'arc rebase --continue' or 'arc rebase --abort'.
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
When the stack is clean, push everything and open PRs:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
arc push # force-pushes all branches atomically
|
|
90
|
+
arc submit --draft # creates or updates PRs for each branch
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Each PR targets the branch below it, not `main` directly. Reviewers see only the diff for that layer. `arc submit` also injects a stack map into every PR description:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
---
|
|
97
|
+
Stack (base: main):
|
|
98
|
+
1. feat/auth - PR #42 [this PR]
|
|
99
|
+
2. feat/api - PR #43
|
|
100
|
+
3. feat/ui - no PR
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Reviewers can navigate the whole stack from any PR without hunting for context.
|
|
104
|
+
|
|
105
|
+
When you're ready to open for review, `arc submit --open` marks all drafts as ready at once.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## When a PR merges
|
|
110
|
+
|
|
111
|
+
Once `feat/auth` is approved and merged on GitHub:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
arc land feat/auth
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`arc` verifies the PR is merged, detects whether it was a squash-merge or a regular merge, rebases `feat/api` and `feat/ui` onto `main` correctly (using `git rebase --onto` for squash-merges, which would otherwise leave duplicate commits), removes `feat/auth` from the stack, and deletes the local branch.
|
|
118
|
+
|
|
119
|
+
You can land branches in order as they get approved. The rest of the stack stays coherent throughout.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Other useful things
|
|
124
|
+
|
|
125
|
+
**If a lower branch needs changes** after review feedback:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
arc checkout feat/auth # or: arc checkout 1
|
|
129
|
+
# fix the issue, amend your commit
|
|
130
|
+
arc sync # cascades the change up through api and ui
|
|
131
|
+
arc push && arc submit
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`arc checkout 2` navigates to the second branch in the stack. `arc up` / `arc down` / `arc top` / `arc bottom` move you through the stack without remembering branch names.
|
|
135
|
+
|
|
136
|
+
**Remove a branch** from the stack without touching the others:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
arc drop feat/api -f # restacks feat/ui onto feat/auth
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Keep commit messages useful** after a PR is created:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
arc amend # appends the PR link and stack position to the HEAD commit message
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This means `git log` on the landed commits still traces back to the PR.
|
|
149
|
+
|
|
150
|
+
**Gate submissions on local checks** — configure `.arc/config.json` (committed, shared with your team):
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"hooks": {
|
|
155
|
+
"pre-submit": ["npm run lint", "npm test"]
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`arc submit` runs these before touching GitHub. Any non-zero exit aborts. Pass `--skip-hooks` to bypass.
|
|
161
|
+
|
|
162
|
+
**Preview destructive operations** before running them:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
arc sync -n # shows the rebase plan without executing
|
|
166
|
+
arc push -n # shows which branches would be pushed
|
|
167
|
+
arc land -n # shows which branches would be restacked
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Scripting and agents
|
|
173
|
+
|
|
174
|
+
Every command is non-interactive by default. `--json` sends structured output to stdout; status messages go to stderr. Exit codes carry meaning.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Find branches that need rebasing
|
|
178
|
+
arc status --json | jq '.branches[] | select(.needs_rebase) | .name'
|
|
179
|
+
|
|
180
|
+
# Get all branch names for a script
|
|
181
|
+
arc status --plain
|
|
182
|
+
|
|
183
|
+
# Full dry-run before committing
|
|
184
|
+
arc sync -n && arc push -n && arc submit -n
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**`arc status --json` output:**
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"base": "main",
|
|
192
|
+
"prefix": "feat",
|
|
193
|
+
"current_branch": "feat/api",
|
|
194
|
+
"branches": [
|
|
195
|
+
{
|
|
196
|
+
"name": "feat/auth",
|
|
197
|
+
"index": 1,
|
|
198
|
+
"pr_number": 42,
|
|
199
|
+
"pr_url": "https://github.com/owner/repo/pull/42",
|
|
200
|
+
"pr_state": "OPEN",
|
|
201
|
+
"commits": 2,
|
|
202
|
+
"revision": 3,
|
|
203
|
+
"needs_rebase": false,
|
|
204
|
+
"is_current": false,
|
|
205
|
+
"is_merged": false
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Exit codes:**
|
|
212
|
+
|
|
213
|
+
| Code | Meaning | What to do |
|
|
214
|
+
|------|---------|------------|
|
|
215
|
+
| 0 | Success | — |
|
|
216
|
+
| 1 | Error | Read stderr |
|
|
217
|
+
| 2 | Not in a stack | `arc init` |
|
|
218
|
+
| 3 | Rebase conflict | Resolve, then `arc rebase --continue` or `--abort` |
|
|
219
|
+
| 4 | GitHub API failure | `gh auth status`, retry |
|
|
220
|
+
| 5 | Invalid arguments | Read stderr |
|
|
221
|
+
| 6 | Setup check failed | `arc setup` |
|
|
222
|
+
| 7 | Pre-submit hook failed | Fix the check or `--skip-hooks` |
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Reference
|
|
227
|
+
|
|
228
|
+
All commands accept `-q` (`--quiet`) to suppress hints and `-n` (`--dry-run`) where the operation is destructive. `--json` is available on any command that produces data.
|
|
229
|
+
|
|
230
|
+
| Command | What it does |
|
|
231
|
+
|---------|-------------|
|
|
232
|
+
| `arc setup` | Verify environment, configure git |
|
|
233
|
+
| `arc init [--base B] [--prefix P]` | Initialize a stack |
|
|
234
|
+
| `arc new <branch>` | Create branch from HEAD, add to stack |
|
|
235
|
+
| `arc add <branch>` | Adopt an existing local branch |
|
|
236
|
+
| `arc status [--json\|--plain]` | Show the stack |
|
|
237
|
+
| `arc sync` | Fetch + cascade rebase |
|
|
238
|
+
| `arc push` | Force-push all branches, increment revision |
|
|
239
|
+
| `arc submit [--draft\|--open] [--skip-hooks]` | Create or update PRs |
|
|
240
|
+
| `arc land [<branch>] [-f]` | Land a merged PR, restack above |
|
|
241
|
+
| `arc amend` | Append PR link to commit message |
|
|
242
|
+
| `arc drop <branch> [-f]` | Remove branch, restack above |
|
|
243
|
+
| `arc rebase [--upstack\|--downstack\|--continue\|--abort]` | Fine-grained rebase |
|
|
244
|
+
| `arc checkout <name\|index>` | Switch to branch by name or position |
|
|
245
|
+
| `arc up [n]` / `arc down [n]` | Move through the stack |
|
|
246
|
+
| `arc top` / `arc bottom` | Jump to ends of the stack |
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
|
File without changes
|