dubstack 0.4.0 → 0.5.1

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.
package/README.md CHANGED
@@ -1,320 +1,400 @@
1
1
  # DubStack
2
2
 
3
- A local-first CLI for managing **stacked diffs** — chains of dependent git branches that build on each other. Stop juggling complex rebase chains by hand.
3
+ DubStack (`dub`) is a local-first CLI for stacked branch workflows.
4
4
 
5
- ## Why Stacked Diffs?
5
+ It is designed for the Graphite mental model: small, dependent PRs that are easy to review, update, and rebase.
6
6
 
7
- Stacked diffs let you break large features into small, reviewable PRs that depend on each other. Instead of one 2,000-line monster PR, you get a clean chain:
7
+ ## Why DubStack
8
8
 
9
- ```
9
+ Large PRs are hard to review and painful to keep up to date.
10
+
11
+ Stacked branches let you split work into focused layers:
12
+
13
+ ```text
10
14
  (main)
11
- └─ feat/api-models
12
- └─ feat/api-endpoint
13
- └─ feat/ui-component
15
+ └─ feat/auth-types
16
+ └─ feat/auth-login
17
+ └─ feat/auth-tests
14
18
  ```
15
19
 
16
- When `main` updates or you amend an earlier branch, `dub restack` cascades rebases through the entire chain for you.
20
+ When a lower branch changes, `dub restack` propagates it upstack.
17
21
 
18
22
  ## Install
19
23
 
20
- **Homebrew (Recommended)**
24
+ ### Homebrew (recommended)
21
25
 
22
26
  ```bash
23
27
  brew tap wiseiodev/dubstack
24
28
  brew install dubstack
25
29
  ```
26
30
 
27
- To update to the latest version:
31
+ Update:
28
32
 
29
33
  ```bash
30
34
  brew upgrade dubstack
31
35
  ```
32
36
 
33
- **npm**
37
+ ### npm
34
38
 
35
39
  ```bash
36
40
  npm install -g dubstack
37
41
  ```
38
42
 
39
- **From Source** (for contributors)
40
-
41
- > Requires **Node ≥ 22** and **pnpm**.
43
+ ### From source
42
44
 
43
45
  ```bash
44
- # Clone and install
45
- git clone https://github.com/wiseiodev/dubstack.git && cd dubstack
46
+ git clone https://github.com/wiseiodev/dubstack.git
47
+ cd dubstack
46
48
  pnpm install
47
-
48
- # Link globally so `dub` is available everywhere
49
49
  pnpm build
50
50
  pnpm link --global
51
51
  ```
52
52
 
53
+ ## Graphite Mental Model
54
+
55
+ If you have `gt` muscle memory, use this as a fast map:
56
+
57
+ | Graphite (`gt`) | DubStack (`dub`) |
58
+ |---|---|
59
+ | `gt create` | `dub create` |
60
+ | `gt modify` | `dub modify` or `dub m` |
61
+ | `gt submit` / `gt ss` | `dub submit` / `dub ss` |
62
+ | `gt sync` | `dub sync` |
63
+ | `gt checkout` / `gt co` | `dub checkout` / `dub co` |
64
+ | `gt log` / `gt ls` | `dub log` / `dub ls` |
65
+ | `gt up` / `gt down` | `dub up` / `dub down` |
66
+ | `gt top` / `gt bottom` | `dub top` / `dub bottom` |
67
+ | `gt info` | `dub info` |
68
+ | `gt pr` | `dub pr` |
69
+ | `gt restack` | `dub restack` |
70
+ | `gt undo` | `dub undo` |
71
+
53
72
  ## Quick Start
54
73
 
55
74
  ```bash
56
- # 1. Initialize in any git repo
57
- cd my-project
58
- dub init
59
-
60
- # 2. Start stacking branches
75
+ # 1) Start from trunk
61
76
  git checkout main
62
- dub create feat/api-models
63
- # hack hack hack, commit...
64
-
65
- dub create feat/api-endpoint
66
- # hack hack hack, commit...
77
+ git pull
67
78
 
68
- dub create feat/ui-component
69
- # hack hack hack, commit...
79
+ # 2) Create stacked branches
80
+ # Create + stage all + commit
81
+ dub create feat/auth-types -am "feat: add auth types"
82
+ dub create feat/auth-login -am "feat: add login flow"
83
+ dub create feat/auth-tests -am "test: add auth tests"
70
84
 
71
- # 3. See your stack
85
+ # 3) View stack
72
86
  dub log
73
87
 
74
- # 4. Rebase the whole chain after main updates
75
- git checkout main && git pull
76
- dub restack
88
+ # 4) Submit stack PRs
89
+ dub ss
77
90
 
78
- # 5. Made a mistake? Undo it
79
- # 5. Made a mistake? Undo it
80
- dub undo
91
+ # 5) Open PR for current branch
92
+ dub pr
81
93
  ```
82
94
 
83
- ## Agent Skills
95
+ For a more detailed walkthrough, see [`QUICKSTART.md`](./QUICKSTART.md).
96
+
97
+ ## Command Reference
84
98
 
85
- DubStack provides skills for AI coding agents (like Cursor, Windsurf, etc.) to use the CLI effectively.
99
+ ### `dub init`
86
100
 
87
- You can install them easily using the `dub skills` command:
101
+ Initialize DubStack state in the current git repository.
88
102
 
89
103
  ```bash
90
- # Install all skills
91
- dub skills add
92
-
93
- # Install specific skill
94
- dub skills add dub-flow
104
+ dub init
95
105
  ```
96
106
 
97
- Alternatively, you can install them manually via `npx`:
107
+ Notes:
108
+ - `dub create` auto-initializes state if needed.
109
+ - Running `dub init` manually is still useful for explicit setup.
110
+
111
+ ### `dub create <branch>`
112
+
113
+ Create a branch stacked on top of the current branch.
98
114
 
99
115
  ```bash
100
- # Core CLI reference & workflows
101
- npx skills add wiseiodev/dubstack/skills/dubstack
116
+ # branch only
117
+ dub create feat/my-change
118
+
119
+ # create + commit staged changes
120
+ dub create feat/my-change -m "feat: ..."
121
+
122
+ # stage all + create + commit
123
+ dub create feat/my-change -am "feat: ..."
102
124
 
103
- # Specialized PR creation workflow
104
- npx skills add wiseiodev/dubstack/skills/dub-flow
125
+ # stage tracked-file updates + create + commit
126
+ dub create feat/my-change -um "feat: ..."
127
+
128
+ # interactive hunk staging + create + commit
129
+ dub create feat/my-change -pm "feat: ..."
105
130
  ```
106
131
 
107
- ## Commands
132
+ Flags:
133
+ - `-m, --message <message>`: commit message
134
+ - `-a, --all`: stage all changes before commit (requires `-m`)
135
+ - `-u, --update`: stage tracked-file updates before commit (requires `-m`)
136
+ - `-p, --patch`: select hunks interactively before commit (requires `-m`)
108
137
 
109
- ### `dub init`
138
+ ### `dub modify` / `dub m`
110
139
 
111
- Initializes DubStack in the current git repository.
140
+ Amend or create commits on the current branch, then restack descendants.
112
141
 
113
142
  ```bash
114
- dub init
115
- ```
143
+ # amend current commit
144
+ dub modify
116
145
 
117
- - Creates `.git/dubstack/state.json` with an empty state
118
- - Adds `.git/dubstack` to `.gitignore`
119
- - **Idempotent** — safe to run multiple times
146
+ # create a new commit
147
+ dub modify -c -m "fix: ..."
120
148
 
121
- ```
122
- DubStack initialized # first run
123
- ⚠ DubStack already initialized # subsequent runs
149
+ # interactive staging
150
+ dub modify -p
151
+
152
+ # stage all tracked updates
153
+ dub modify -u
154
+
155
+ # show staged diff before modify
156
+ dub modify -v
157
+
158
+ # show staged + unstaged diff before modify
159
+ dub modify -vv
160
+
161
+ # interactive rebase of this branch's commits
162
+ dub modify --interactive-rebase
124
163
  ```
125
164
 
126
- ---
165
+ Flags:
166
+ - `-a, --all`
167
+ - `-u, --update`
168
+ - `-p, --patch`
169
+ - `-c, --commit`
170
+ - `-e, --edit`
171
+ - `-m, --message <message>` (repeatable)
172
+ - `-v, --verbose` (repeatable)
173
+ - `--interactive-rebase`
127
174
 
128
- ### `dub create <branch-name>`
175
+ ### `dub checkout` / `dub co`
129
176
 
130
- Creates a new branch stacked on top of the current branch.
177
+ Checkout a branch directly or use interactive search.
131
178
 
132
179
  ```bash
133
- # On main
134
- dub create feat/api-models
180
+ # checkout explicit branch
181
+ dub checkout feat/auth-login
135
182
 
136
- # On feat/api-models
137
- dub create feat/api-endpoint
138
- ```
183
+ # interactive picker
184
+ dub checkout
139
185
 
140
- - Checks out the new branch at the current HEAD
141
- - Records the parent → child relationship in state
142
- - Auto-creates a new stack if the parent isn't already tracked
143
- - Saves an undo snapshot before any mutation
186
+ # checkout trunk for current tracked stack
187
+ dub checkout --trunk
144
188
 
145
- **Errors:**
146
- | Condition | Message |
147
- |---|---|
148
- | Not initialized | `DubStack is not initialized. Run 'dub init' first.` |
149
- | Branch already exists | `Branch '<name>' already exists.` |
150
- | Detached HEAD | `HEAD is detached. Check out a branch first.` |
189
+ # interactive picker including non-tracked local branches
190
+ dub checkout --show-untracked
151
191
 
152
- ---
192
+ # interactive picker scoped to current stack
193
+ dub checkout --stack
194
+ ```
153
195
 
154
- ### `dub log`
196
+ ### `dub log` / `dub ls` / `dub l`
155
197
 
156
- Displays an ASCII tree of all tracked stacks.
198
+ Render tracked stacks as an ASCII tree.
157
199
 
158
200
  ```bash
159
201
  dub log
202
+ dub ls
203
+ dub l
160
204
  ```
161
205
 
162
- Example output:
206
+ ### Navigation: `dub up`, `dub down`, `dub top`, `dub bottom`
163
207
 
164
- ```
165
- (main)
166
- ├─ feat/api-models
167
- │ └─ feat/api-endpoint (Current)
168
- └─ feat/auth
169
- └─ feat/auth-ui ⚠ (missing)
170
- ```
208
+ ```bash
209
+ # move one branch upstack
210
+ dub up
171
211
 
172
- - **Current branch** is highlighted and marked `(Current)`
173
- - **Root branches** are shown in parentheses, e.g. `(main)`
174
- - **Deleted branches** still tracked in state show `⚠ (missing)`
175
- - Multiple stacks are separated by blank lines
212
+ # move multiple levels upstack
213
+ dub up 2
214
+ # or: dub up --steps 2
176
215
 
177
- ---
216
+ # move downstack
217
+ dub down
218
+ dub down 2
178
219
 
179
- ### `dub restack`
220
+ # jump to tip branch in current path
221
+ dub top
180
222
 
181
- Rebases all branches in the current stack onto their updated parents.
182
-
183
- ```bash
184
- dub restack
223
+ # jump to first branch above root
224
+ dub bottom
185
225
  ```
186
226
 
187
- **How it works:**
227
+ ### `dub info` and `dub branch info`
188
228
 
189
- 1. Snapshots every branch tip _before_ starting
190
- 2. Walks the tree in topological order (parents first)
191
- 3. For each child branch, runs `git rebase --onto <parent_new_tip> <parent_old_tip> <child>`
192
- 4. Skips branches whose parent hasn't moved
193
- 5. Returns you to the branch you started on
229
+ Show tracked metadata for a branch.
194
230
 
195
- **Conflict handling:**
231
+ ```bash
232
+ # current branch
233
+ dub info
196
234
 
197
- If a rebase hits a conflict, DubStack pauses and tells you:
235
+ # explicit branch
236
+ dub info feat/auth-login
198
237
 
199
- ```
200
- Conflict while restacking 'feat/api-endpoint'
201
- Resolve conflicts, stage changes, then run: dub restack --continue
238
+ # equivalent legacy style
239
+ dub branch info
202
240
  ```
203
241
 
204
- After resolving:
242
+ ### `dub submit` / `dub ss`
243
+
244
+ Push stack branches and create or update PRs.
205
245
 
206
246
  ```bash
207
- # Fix the conflicting files
208
- git add .
209
- dub restack --continue
247
+ dub submit
248
+ dub ss
249
+
250
+ # preview only
251
+ dub submit --dry-run
210
252
  ```
211
253
 
212
- Progress is saved to `.git/dubstack/restack-progress.json`, so the resume picks up exactly where it left off.
254
+ ### `dub pr [branch-or-number]`
213
255
 
214
- **Output examples:**
256
+ Open a PR in browser via `gh`.
215
257
 
258
+ ```bash
259
+ # current branch PR
260
+ dub pr
261
+
262
+ # explicit branch / PR target
263
+ dub pr feat/auth-login
264
+ dub pr 123
216
265
  ```
217
- ✔ Stack is already up to date
218
266
 
219
- Restacked 2 branch(es)
220
- ↳ feat/api-models
221
- feat/api-endpoint
267
+ ### `dub sync`
268
+
269
+ Synchronize tracked branches with remote refs, then optionally restack.
270
+
271
+ ```bash
272
+ # sync current stack
273
+ dub sync
274
+
275
+ # sync all tracked stacks
276
+ dub sync --all
277
+
278
+ # non-interactive mode
279
+ dub sync --no-interactive
280
+
281
+ # force destructive sync decisions
282
+ dub sync --force
283
+
284
+ # skip post-sync restack
285
+ dub sync --no-restack
222
286
  ```
223
287
 
224
- **Errors:**
225
- | Condition | Message |
226
- |---|---|
227
- | Uncommitted changes | `Working tree has uncommitted changes. Commit or stash them before restacking.` |
228
- | Branch not in a stack | `Branch '<name>' is not part of any stack.` |
229
- | Tracked branch deleted | `Branch '<name>' is tracked in state but no longer exists in git.` |
288
+ Current sync behavior includes:
289
+ - fetch tracked refs from `origin`
290
+ - attempt trunk fast-forward (or overwrite with `--force`)
291
+ - cleanup for merged/closed PR branches whose commits are confirmed in trunk
292
+ - reconcile local/remote divergence states per branch
293
+ - restack by default after sync
294
+
295
+ ### `dub restack`
230
296
 
231
- ---
297
+ Rebase stack branches onto updated parents.
298
+
299
+ ```bash
300
+ dub restack
301
+
302
+ # continue after resolving conflicts
303
+ dub restack --continue
304
+ ```
232
305
 
233
306
  ### `dub undo`
234
307
 
235
- Rolls back the last `dub create` or `dub restack` operation.
308
+ Undo last `dub create` or `dub restack` operation.
236
309
 
237
310
  ```bash
238
311
  dub undo
239
312
  ```
240
313
 
241
- **Undo strategies:**
314
+ ### `dub skills`
242
315
 
243
- - **After `create`:** Deletes the created branch, restores state, checks out the previous branch
244
- - **After `restack`:** Force-resets every rebased branch to its pre-rebase commit, restores state
316
+ Install or remove packaged agent skills.
245
317
 
246
- Only **one level** of undo is supported. After undo, the undo entry is cleared.
318
+ ```bash
319
+ # install all bundled skills
320
+ dub skills add
247
321
 
248
- ```
249
- Undid 'create': Deleted branch 'feat/api-endpoint'
250
- ✔ Undid 'restack': Reset 3 branches to pre-restack state
322
+ # install one skill
323
+ dub skills add dubstack
324
+
325
+ # remove one skill
326
+ dub skills remove dub-flow
327
+
328
+ # preview without changing anything
329
+ dub skills add --dry-run
330
+ dub skills remove --dry-run
251
331
  ```
252
332
 
253
- **Errors:**
254
- | Condition | Message |
255
- |---|---|
256
- | Nothing to undo | `Nothing to undo.` |
257
- | Uncommitted changes | `Working tree has uncommitted changes. Commit or stash them before undoing.` |
333
+ ## Typical Workflows
334
+
335
+ ### Add review feedback to a middle branch
258
336
 
259
- ---
337
+ ```bash
338
+ # jump to branch needing edits
339
+ dub co feat/auth-login
260
340
 
261
- ## Typical Workflow
341
+ # edit + amend + restack descendants
342
+ dub m -a -m "fix: address feedback"
343
+
344
+ # resubmit stack
345
+ dub ss
346
+ ```
347
+
348
+ ### Sync after trunk moves
262
349
 
263
350
  ```bash
264
- # Start a feature stack off main
265
351
  git checkout main
266
- dub create feat/data-layer
267
- # write code, commit
268
-
269
- dub create feat/api-routes
270
- # write code, commit
352
+ git pull
353
+ dub sync
354
+ ```
271
355
 
272
- dub create feat/frontend
273
- # write code, commit
356
+ ### Recover from restack conflict
274
357
 
275
- # View the stack
276
- dub log
277
- # (main)
278
- # └─ feat/data-layer
279
- # └─ feat/api-routes
280
- # └─ feat/frontend (Current)
281
-
282
- # Later: main gets updated, or you amend feat/data-layer
283
- git checkout feat/data-layer
284
- # amend your commits...
358
+ ```bash
285
359
  dub restack
286
- # Restacked 2 branch(es)
287
- # ↳ feat/api-routes
288
- # ↳ feat/frontend
289
-
290
- # Oops, that restack went wrong
291
- dub undo
292
- # ✔ Undid 'restack': Reset 3 branches to pre-restack state
360
+ # resolve conflicts
361
+ git add <resolved-files>
362
+ dub restack --continue
293
363
  ```
294
364
 
295
- ## How State Works
365
+ ## Troubleshooting
296
366
 
297
- DubStack stores all state locally inside your git repo:
367
+ | Problem | What to do |
368
+ |---|---|
369
+ | `gh CLI not found` | Install GitHub CLI: https://cli.github.com |
370
+ | `Not authenticated with GitHub` | Run `gh auth login` |
371
+ | Branch not part of stack | Create via `dub create` or run from tracked branch |
372
+ | Restack conflict | Resolve files, `git add`, `dub restack --continue` |
373
+ | Sync skipped branch | Re-run with `--interactive` or `--force` as appropriate |
374
+ | Wrong operation during create/restack | Use `dub undo` (single-level) |
298
375
 
299
- ```
376
+ ## State Files
377
+
378
+ DubStack stores local state in your repo:
379
+
380
+ ```text
300
381
  .git/dubstack/
301
- ├── state.json # branch relationships and stack metadata
302
- ├── undo.json # snapshot for single-level undo
303
- └── restack-progress.json # in-flight restack state (temporary)
382
+ ├── state.json
383
+ ├── undo.json
384
+ └── restack-progress.json
304
385
  ```
305
386
 
306
- Nothing is pushed to your remote. State is per-repo and git-ignored.
387
+ Nothing is pushed to your remote from these files.
307
388
 
308
389
  ## Development
309
390
 
310
391
  ```bash
311
- pnpm install # install deps
312
- pnpm dev # run via tsx (no build step)
313
- pnpm build # compile TypeScript to dist/
314
- pnpm test # run tests (vitest)
315
- pnpm typecheck # type-check without emitting
316
- pnpm checks # lint + format check (biome)
317
- pnpm checks:fix # auto-fix lint + format issues
392
+ pnpm install
393
+ pnpm test
394
+ pnpm typecheck
395
+ pnpm checks
396
+ pnpm checks:fix
397
+ pnpm build
318
398
  ```
319
399
 
320
400
  ## License