dubstack 0.5.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.
Files changed (2) hide show
  1. package/README.md +249 -211
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,362 +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
77
-
78
- # 4b. Sync local stack state with remote branches
79
- dub sync
88
+ # 4) Submit stack PRs
89
+ dub ss
80
90
 
81
- # 5. Made a mistake? Undo it
82
- # 5. Made a mistake? Undo it
83
- dub undo
91
+ # 5) Open PR for current branch
92
+ dub pr
84
93
  ```
85
94
 
86
- ## Agent Skills
95
+ For a more detailed walkthrough, see [`QUICKSTART.md`](./QUICKSTART.md).
87
96
 
88
- DubStack provides skills for AI coding agents (like Cursor, Windsurf, etc.) to use the CLI effectively.
97
+ ## Command Reference
89
98
 
90
- You can install them easily using the `dub skills` command:
99
+ ### `dub init`
91
100
 
92
- ```bash
93
- # Install all skills
94
- dub skills add
101
+ Initialize DubStack state in the current git repository.
95
102
 
96
- # Install specific skill
97
- dub skills add dub-flow
103
+ ```bash
104
+ dub init
98
105
  ```
99
106
 
100
- 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.
101
114
 
102
115
  ```bash
103
- # Core CLI reference & workflows
104
- npx skills add wiseiodev/dubstack/skills/dubstack
116
+ # branch only
117
+ dub create feat/my-change
105
118
 
106
- # Specialized PR creation workflow
107
- npx skills add wiseiodev/dubstack/skills/dub-flow
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: ..."
124
+
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: ..."
108
130
  ```
109
131
 
110
- ## 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`)
111
137
 
112
- ### `dub init`
138
+ ### `dub modify` / `dub m`
113
139
 
114
- Initializes DubStack in the current git repository.
140
+ Amend or create commits on the current branch, then restack descendants.
115
141
 
116
142
  ```bash
117
- dub init
118
- ```
143
+ # amend current commit
144
+ dub modify
119
145
 
120
- - Creates `.git/dubstack/state.json` with an empty state
121
- - Adds `.git/dubstack` to `.gitignore`
122
- - **Idempotent** — safe to run multiple times
146
+ # create a new commit
147
+ dub modify -c -m "fix: ..."
123
148
 
124
- ```
125
- DubStack initialized # first run
126
- ⚠ 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
127
163
  ```
128
164
 
129
- ---
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`
130
174
 
131
- ### `dub create <branch-name>`
175
+ ### `dub checkout` / `dub co`
132
176
 
133
- Creates a new branch stacked on top of the current branch.
177
+ Checkout a branch directly or use interactive search.
134
178
 
135
179
  ```bash
136
- # On main
137
- dub create feat/api-models
180
+ # checkout explicit branch
181
+ dub checkout feat/auth-login
138
182
 
139
- # On feat/api-models
140
- dub create feat/api-endpoint
141
- ```
183
+ # interactive picker
184
+ dub checkout
142
185
 
143
- - Checks out the new branch at the current HEAD
144
- - Records the parent → child relationship in state
145
- - Auto-creates a new stack if the parent isn't already tracked
146
- - Saves an undo snapshot before any mutation
186
+ # checkout trunk for current tracked stack
187
+ dub checkout --trunk
147
188
 
148
- **Errors:**
149
- | Condition | Message |
150
- |---|---|
151
- | Not initialized | `DubStack is not initialized. Run 'dub init' first.` |
152
- | Branch already exists | `Branch '<name>' already exists.` |
153
- | Detached HEAD | `HEAD is detached. Check out a branch first.` |
189
+ # interactive picker including non-tracked local branches
190
+ dub checkout --show-untracked
154
191
 
155
- ---
192
+ # interactive picker scoped to current stack
193
+ dub checkout --stack
194
+ ```
156
195
 
157
- ### `dub log`
196
+ ### `dub log` / `dub ls` / `dub l`
158
197
 
159
- Displays an ASCII tree of all tracked stacks.
198
+ Render tracked stacks as an ASCII tree.
160
199
 
161
200
  ```bash
162
201
  dub log
202
+ dub ls
203
+ dub l
163
204
  ```
164
205
 
165
- Example output:
166
-
167
- ```
168
- (main)
169
- ├─ feat/api-models
170
- │ └─ feat/api-endpoint (Current)
171
- └─ feat/auth
172
- └─ feat/auth-ui ⚠ (missing)
173
- ```
206
+ ### Navigation: `dub up`, `dub down`, `dub top`, `dub bottom`
174
207
 
175
- - **Current branch** is highlighted and marked `(Current)`
176
- - **Root branches** are shown in parentheses, e.g. `(main)`
177
- - **Deleted branches** still tracked in state show `⚠ (missing)`
178
- - Multiple stacks are separated by blank lines
208
+ ```bash
209
+ # move one branch upstack
210
+ dub up
179
211
 
180
- ---
212
+ # move multiple levels upstack
213
+ dub up 2
214
+ # or: dub up --steps 2
181
215
 
182
- ### `dub restack`
216
+ # move downstack
217
+ dub down
218
+ dub down 2
183
219
 
184
- Rebases all branches in the current stack onto their updated parents.
220
+ # jump to tip branch in current path
221
+ dub top
185
222
 
186
- ```bash
187
- dub restack
223
+ # jump to first branch above root
224
+ dub bottom
188
225
  ```
189
226
 
190
- **How it works:**
227
+ ### `dub info` and `dub branch info`
191
228
 
192
- 1. Snapshots every branch tip _before_ starting
193
- 2. Walks the tree in topological order (parents first)
194
- 3. For each child branch, runs `git rebase --onto <parent_new_tip> <parent_old_tip> <child>`
195
- 4. Skips branches whose parent hasn't moved
196
- 5. Returns you to the branch you started on
229
+ Show tracked metadata for a branch.
197
230
 
198
- **Conflict handling:**
231
+ ```bash
232
+ # current branch
233
+ dub info
199
234
 
200
- If a rebase hits a conflict, DubStack pauses and tells you:
235
+ # explicit branch
236
+ dub info feat/auth-login
201
237
 
238
+ # equivalent legacy style
239
+ dub branch info
202
240
  ```
203
- ⚠ Conflict while restacking 'feat/api-endpoint'
204
- Resolve conflicts, stage changes, then run: dub restack --continue
205
- ```
206
241
 
207
- After resolving:
242
+ ### `dub submit` / `dub ss`
243
+
244
+ Push stack branches and create or update PRs.
208
245
 
209
246
  ```bash
210
- # Fix the conflicting files
211
- git add .
212
- dub restack --continue
247
+ dub submit
248
+ dub ss
249
+
250
+ # preview only
251
+ dub submit --dry-run
213
252
  ```
214
253
 
215
- 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]`
216
255
 
217
- **Output examples:**
256
+ Open a PR in browser via `gh`.
218
257
 
219
- ```
220
- Stack is already up to date
258
+ ```bash
259
+ # current branch PR
260
+ dub pr
221
261
 
222
- Restacked 2 branch(es)
223
- feat/api-models
224
- feat/api-endpoint
262
+ # explicit branch / PR target
263
+ dub pr feat/auth-login
264
+ dub pr 123
225
265
  ```
226
266
 
227
- **Errors:**
228
- | Condition | Message |
229
- |---|---|
230
- | Uncommitted changes | `Working tree has uncommitted changes. Commit or stash them before restacking.` |
231
- | Branch not in a stack | `Branch '<name>' is not part of any stack.` |
232
- | Tracked branch deleted | `Branch '<name>' is tracked in state but no longer exists in git.` |
233
-
234
- ---
235
-
236
267
  ### `dub sync`
237
268
 
238
- Synchronizes tracked stack branches with remote refs and optionally restacks.
269
+ Synchronize tracked branches with remote refs, then optionally restack.
239
270
 
240
271
  ```bash
272
+ # sync current stack
241
273
  dub sync
242
- ```
243
-
244
- Useful flags:
245
274
 
246
- ```bash
247
- # Sync all stacks across trunks
275
+ # sync all tracked stacks
248
276
  dub sync --all
249
277
 
250
- # Non-interactive safe mode (skip destructive operations)
278
+ # non-interactive mode
251
279
  dub sync --no-interactive
252
280
 
253
- # Force overwrite/delete decisions during sync
281
+ # force destructive sync decisions
254
282
  dub sync --force
255
283
 
256
- # Skip post-sync restack
284
+ # skip post-sync restack
257
285
  dub sync --no-restack
258
286
  ```
259
287
 
260
- Current behavior:
261
- - Fetches tracked roots and branches from `origin`
262
- - Attempts fast-forward trunk updates (or overwrite with `--force`)
263
- - Cleans local branches whose PRs are `MERGED`/`CLOSED` and whose commits are
264
- confirmed in trunk (interactive/force), while keeping non-cleanable stacks
265
- excluded from destructive sync actions
266
- - Reconciles each tracked branch:
267
- - restores missing local branches from remote
268
- - fast-forwards branches that are safely behind remote
269
- - keeps local-ahead branches
270
- - prompts/skips/forces on diverged branches
271
- - Runs post-sync restack by default
272
-
273
- ---
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`
296
+
297
+ Rebase stack branches onto updated parents.
298
+
299
+ ```bash
300
+ dub restack
301
+
302
+ # continue after resolving conflicts
303
+ dub restack --continue
304
+ ```
274
305
 
275
306
  ### `dub undo`
276
307
 
277
- Rolls back the last `dub create` or `dub restack` operation.
308
+ Undo last `dub create` or `dub restack` operation.
278
309
 
279
310
  ```bash
280
311
  dub undo
281
312
  ```
282
313
 
283
- **Undo strategies:**
314
+ ### `dub skills`
315
+
316
+ Install or remove packaged agent skills.
317
+
318
+ ```bash
319
+ # install all bundled skills
320
+ dub skills add
284
321
 
285
- - **After `create`:** Deletes the created branch, restores state, checks out the previous branch
286
- - **After `restack`:** Force-resets every rebased branch to its pre-rebase commit, restores state
322
+ # install one skill
323
+ dub skills add dubstack
287
324
 
288
- Only **one level** of undo is supported. After undo, the undo entry is cleared.
325
+ # remove one skill
326
+ dub skills remove dub-flow
289
327
 
290
- ```
291
- Undid 'create': Deleted branch 'feat/api-endpoint'
292
- Undid 'restack': Reset 3 branches to pre-restack state
328
+ # preview without changing anything
329
+ dub skills add --dry-run
330
+ dub skills remove --dry-run
293
331
  ```
294
332
 
295
- **Errors:**
296
- | Condition | Message |
297
- |---|---|
298
- | Nothing to undo | `Nothing to undo.` |
299
- | 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
336
+
337
+ ```bash
338
+ # jump to branch needing edits
339
+ dub co feat/auth-login
340
+
341
+ # edit + amend + restack descendants
342
+ dub m -a -m "fix: address feedback"
300
343
 
301
- ---
344
+ # resubmit stack
345
+ dub ss
346
+ ```
302
347
 
303
- ## Typical Workflow
348
+ ### Sync after trunk moves
304
349
 
305
350
  ```bash
306
- # Start a feature stack off main
307
351
  git checkout main
308
- dub create feat/data-layer
309
- # write code, commit
310
-
311
- dub create feat/api-routes
312
- # write code, commit
352
+ git pull
353
+ dub sync
354
+ ```
313
355
 
314
- dub create feat/frontend
315
- # write code, commit
356
+ ### Recover from restack conflict
316
357
 
317
- # View the stack
318
- dub log
319
- # (main)
320
- # └─ feat/data-layer
321
- # └─ feat/api-routes
322
- # └─ feat/frontend (Current)
323
-
324
- # Later: main gets updated, or you amend feat/data-layer
325
- git checkout feat/data-layer
326
- # amend your commits...
358
+ ```bash
327
359
  dub restack
328
- # Restacked 2 branch(es)
329
- # ↳ feat/api-routes
330
- # ↳ feat/frontend
331
-
332
- # Oops, that restack went wrong
333
- dub undo
334
- # ✔ Undid 'restack': Reset 3 branches to pre-restack state
360
+ # resolve conflicts
361
+ git add <resolved-files>
362
+ dub restack --continue
335
363
  ```
336
364
 
337
- ## How State Works
365
+ ## Troubleshooting
338
366
 
339
- 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) |
340
375
 
341
- ```
376
+ ## State Files
377
+
378
+ DubStack stores local state in your repo:
379
+
380
+ ```text
342
381
  .git/dubstack/
343
- ├── state.json # branch relationships and stack metadata
344
- ├── undo.json # snapshot for single-level undo
345
- └── restack-progress.json # in-flight restack state (temporary)
382
+ ├── state.json
383
+ ├── undo.json
384
+ └── restack-progress.json
346
385
  ```
347
386
 
348
- Nothing is pushed to your remote. State is per-repo and git-ignored.
387
+ Nothing is pushed to your remote from these files.
349
388
 
350
389
  ## Development
351
390
 
352
391
  ```bash
353
- pnpm install # install deps
354
- pnpm dev # run via tsx (no build step)
355
- pnpm build # compile TypeScript to dist/
356
- pnpm test # run tests (vitest)
357
- pnpm typecheck # type-check without emitting
358
- pnpm checks # lint + format check (biome)
359
- 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
360
398
  ```
361
399
 
362
400
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dubstack",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "CLI tool for managing stacked diffs (dependent git branches)",
5
5
  "type": "module",
6
6
  "bin": {