dubstack 0.2.0 → 0.4.0
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 +25 -0
- package/dist/index.js +236 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -75,10 +75,35 @@ dub log
|
|
|
75
75
|
git checkout main && git pull
|
|
76
76
|
dub restack
|
|
77
77
|
|
|
78
|
+
# 5. Made a mistake? Undo it
|
|
78
79
|
# 5. Made a mistake? Undo it
|
|
79
80
|
dub undo
|
|
80
81
|
```
|
|
81
82
|
|
|
83
|
+
## Agent Skills
|
|
84
|
+
|
|
85
|
+
DubStack provides skills for AI coding agents (like Cursor, Windsurf, etc.) to use the CLI effectively.
|
|
86
|
+
|
|
87
|
+
You can install them easily using the `dub skills` command:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Install all skills
|
|
91
|
+
dub skills add
|
|
92
|
+
|
|
93
|
+
# Install specific skill
|
|
94
|
+
dub skills add dub-flow
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Alternatively, you can install them manually via `npx`:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Core CLI reference & workflows
|
|
101
|
+
npx skills add wiseiodev/dubstack/skills/dubstack
|
|
102
|
+
|
|
103
|
+
# Specialized PR creation workflow
|
|
104
|
+
npx skills add wiseiodev/dubstack/skills/dub-flow
|
|
105
|
+
```
|
|
106
|
+
|
|
82
107
|
## Commands
|
|
83
108
|
|
|
84
109
|
### `dub init`
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,118 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/lib/errors.ts
|
|
13
|
+
var DubError;
|
|
14
|
+
var init_errors = __esm({
|
|
15
|
+
"src/lib/errors.ts"() {
|
|
16
|
+
"use strict";
|
|
17
|
+
DubError = class extends Error {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "DubError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// src/lib/skills.ts
|
|
27
|
+
function getSkillRemote(name) {
|
|
28
|
+
return AVAILABLE_SKILLS[name];
|
|
29
|
+
}
|
|
30
|
+
var AVAILABLE_SKILLS;
|
|
31
|
+
var init_skills = __esm({
|
|
32
|
+
"src/lib/skills.ts"() {
|
|
33
|
+
"use strict";
|
|
34
|
+
AVAILABLE_SKILLS = {
|
|
35
|
+
dubstack: "wiseiodev/dubstack/skills/dubstack",
|
|
36
|
+
"dub-flow": "wiseiodev/dubstack/skills/dub-flow"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// src/commands/skills.ts
|
|
42
|
+
var skills_exports = {};
|
|
43
|
+
__export(skills_exports, {
|
|
44
|
+
addSkills: () => addSkills,
|
|
45
|
+
removeSkills: () => removeSkills
|
|
46
|
+
});
|
|
47
|
+
import chalk from "chalk";
|
|
48
|
+
import { execa as execa3 } from "execa";
|
|
49
|
+
function validateSkills(skills) {
|
|
50
|
+
const invalidSkills = skills.filter((s) => !(s in AVAILABLE_SKILLS));
|
|
51
|
+
if (invalidSkills.length > 0) {
|
|
52
|
+
throw new DubError(
|
|
53
|
+
`Unknown skill(s): ${invalidSkills.join(", ")}. Available skills: ${Object.keys(AVAILABLE_SKILLS).join(", ")}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return skills;
|
|
57
|
+
}
|
|
58
|
+
async function addSkills(skills, options = {}) {
|
|
59
|
+
const targets = skills.length > 0 ? validateSkills(skills) : Object.keys(AVAILABLE_SKILLS);
|
|
60
|
+
console.log(chalk.blue(`Adding ${targets.length} skill(s)...`));
|
|
61
|
+
for (const skill of targets) {
|
|
62
|
+
const remote = getSkillRemote(skill);
|
|
63
|
+
const args = ["skills", "add", remote];
|
|
64
|
+
if (options.global) args.push("--global");
|
|
65
|
+
const command = `npx ${args.join(" ")}`;
|
|
66
|
+
console.log(chalk.dim(`Running: ${command}`));
|
|
67
|
+
if (!options.dryRun) {
|
|
68
|
+
try {
|
|
69
|
+
await execa3("npx", args, { stdio: "inherit" });
|
|
70
|
+
console.log(chalk.green(`\u2714 Added skill: ${skill}`));
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(chalk.red(`\u2716 Failed to add skill: ${skill}`));
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function removeSkills(skills, options = {}) {
|
|
79
|
+
const targets = skills.length > 0 ? validateSkills(skills) : Object.keys(AVAILABLE_SKILLS);
|
|
80
|
+
console.log(chalk.blue(`Removing ${targets.length} skill(s)...`));
|
|
81
|
+
for (const skill of targets) {
|
|
82
|
+
const args = ["skills", "remove", skill];
|
|
83
|
+
if (options.global) args.push("--global");
|
|
84
|
+
const command = `npx ${args.join(" ")}`;
|
|
85
|
+
console.log(chalk.dim(`Running: ${command}`));
|
|
86
|
+
if (!options.dryRun) {
|
|
87
|
+
try {
|
|
88
|
+
await execa3("npx", args, { stdio: "inherit" });
|
|
89
|
+
console.log(chalk.green(`\u2714 Removed skill: ${skill}`));
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error(chalk.red(`\u2716 Failed to remove skill: ${skill}`));
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
var init_skills2 = __esm({
|
|
98
|
+
"src/commands/skills.ts"() {
|
|
99
|
+
"use strict";
|
|
100
|
+
init_errors();
|
|
101
|
+
init_skills();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
2
104
|
|
|
3
105
|
// src/index.ts
|
|
4
106
|
import { createRequire } from "module";
|
|
5
|
-
import
|
|
107
|
+
import chalk2 from "chalk";
|
|
6
108
|
import { Command } from "commander";
|
|
7
109
|
|
|
8
|
-
// src/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
super(message);
|
|
12
|
-
this.name = "DubError";
|
|
13
|
-
}
|
|
14
|
-
};
|
|
110
|
+
// src/commands/checkout.ts
|
|
111
|
+
init_errors();
|
|
112
|
+
import search from "@inquirer/search";
|
|
15
113
|
|
|
16
114
|
// src/lib/git.ts
|
|
115
|
+
init_errors();
|
|
17
116
|
import { execa } from "execa";
|
|
18
117
|
async function isGitRepo(cwd) {
|
|
19
118
|
try {
|
|
@@ -201,8 +300,21 @@ async function commitStaged(message, cwd) {
|
|
|
201
300
|
);
|
|
202
301
|
}
|
|
203
302
|
}
|
|
303
|
+
async function listBranches(cwd) {
|
|
304
|
+
try {
|
|
305
|
+
const { stdout } = await execa(
|
|
306
|
+
"git",
|
|
307
|
+
["branch", "--format=%(refname:short)"],
|
|
308
|
+
{ cwd }
|
|
309
|
+
);
|
|
310
|
+
return stdout.trim().split("\n").filter(Boolean);
|
|
311
|
+
} catch {
|
|
312
|
+
throw new DubError("Failed to list branches.");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
204
315
|
|
|
205
316
|
// src/lib/state.ts
|
|
317
|
+
init_errors();
|
|
206
318
|
import * as crypto from "crypto";
|
|
207
319
|
import * as fs from "fs";
|
|
208
320
|
import * as path from "path";
|
|
@@ -315,7 +427,81 @@ function topologicalOrder(stack) {
|
|
|
315
427
|
return result;
|
|
316
428
|
}
|
|
317
429
|
|
|
430
|
+
// src/commands/checkout.ts
|
|
431
|
+
function getTrackedBranches(state) {
|
|
432
|
+
const names = /* @__PURE__ */ new Set();
|
|
433
|
+
for (const stack of state.stacks) {
|
|
434
|
+
for (const branch of stack.branches) {
|
|
435
|
+
names.add(branch.name);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return [...names].sort();
|
|
439
|
+
}
|
|
440
|
+
function getValidBranches(tracked, local) {
|
|
441
|
+
const localSet = new Set(local);
|
|
442
|
+
return tracked.filter((b) => localSet.has(b));
|
|
443
|
+
}
|
|
444
|
+
async function checkout(name, cwd) {
|
|
445
|
+
await checkoutBranch(name, cwd);
|
|
446
|
+
return { branch: name };
|
|
447
|
+
}
|
|
448
|
+
async function interactiveCheckout(cwd) {
|
|
449
|
+
const state = await readState(cwd);
|
|
450
|
+
const trackedBranches = getTrackedBranches(state);
|
|
451
|
+
const localBranches = await listBranches(cwd);
|
|
452
|
+
const validBranches = getValidBranches(trackedBranches, localBranches);
|
|
453
|
+
if (validBranches.length === 0) {
|
|
454
|
+
throw new DubError(
|
|
455
|
+
"No valid tracked branches found. Run 'dub create' first."
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
let currentBranch = null;
|
|
459
|
+
try {
|
|
460
|
+
currentBranch = await getCurrentBranch(cwd);
|
|
461
|
+
} catch {
|
|
462
|
+
}
|
|
463
|
+
const controller = new AbortController();
|
|
464
|
+
const onKeypress = (_str, key) => {
|
|
465
|
+
if (key && key.name === "escape") {
|
|
466
|
+
controller.abort();
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
process.stdin.on("keypress", onKeypress);
|
|
470
|
+
try {
|
|
471
|
+
const selected = await search(
|
|
472
|
+
{
|
|
473
|
+
message: "Checkout a branch (autocomplete or arrow keys)",
|
|
474
|
+
source(term) {
|
|
475
|
+
const filtered = term ? validBranches.filter(
|
|
476
|
+
(b) => b.toLowerCase().includes(term.toLowerCase())
|
|
477
|
+
) : validBranches;
|
|
478
|
+
return filtered.map((name) => ({
|
|
479
|
+
name,
|
|
480
|
+
value: name,
|
|
481
|
+
disabled: name === currentBranch ? "(current)" : false
|
|
482
|
+
}));
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
{ signal: controller.signal }
|
|
486
|
+
);
|
|
487
|
+
return checkout(selected, cwd);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
if (error instanceof Error) {
|
|
490
|
+
if (error.name === "ExitPromptError" || error.name === "AbortError" || error.name === "AbortPromptError") {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
throw error;
|
|
495
|
+
} finally {
|
|
496
|
+
process.stdin.off("keypress", onKeypress);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// src/commands/create.ts
|
|
501
|
+
init_errors();
|
|
502
|
+
|
|
318
503
|
// src/lib/undo-log.ts
|
|
504
|
+
init_errors();
|
|
319
505
|
import * as fs2 from "fs";
|
|
320
506
|
import * as path2 from "path";
|
|
321
507
|
async function getUndoPath(cwd) {
|
|
@@ -390,6 +576,7 @@ async function create(name, cwd, options) {
|
|
|
390
576
|
}
|
|
391
577
|
|
|
392
578
|
// src/commands/init.ts
|
|
579
|
+
init_errors();
|
|
393
580
|
import * as fs3 from "fs";
|
|
394
581
|
import * as path3 from "path";
|
|
395
582
|
async function init(cwd) {
|
|
@@ -489,6 +676,7 @@ async function renderNode(branch, currentBranch, childMap, prefix, isRoot, isLas
|
|
|
489
676
|
}
|
|
490
677
|
|
|
491
678
|
// src/commands/restack.ts
|
|
679
|
+
init_errors();
|
|
492
680
|
import * as fs4 from "fs";
|
|
493
681
|
import * as path4 from "path";
|
|
494
682
|
async function restack(cwd) {
|
|
@@ -636,11 +824,13 @@ async function clearProgress(cwd) {
|
|
|
636
824
|
}
|
|
637
825
|
|
|
638
826
|
// src/commands/submit.ts
|
|
827
|
+
init_errors();
|
|
639
828
|
import * as fs5 from "fs";
|
|
640
829
|
import * as os from "os";
|
|
641
830
|
import * as path5 from "path";
|
|
642
831
|
|
|
643
832
|
// src/lib/github.ts
|
|
833
|
+
init_errors();
|
|
644
834
|
import { execa as execa2 } from "execa";
|
|
645
835
|
async function ensureGhInstalled() {
|
|
646
836
|
try {
|
|
@@ -927,6 +1117,7 @@ function cleanupTempFile(filePath) {
|
|
|
927
1117
|
}
|
|
928
1118
|
|
|
929
1119
|
// src/commands/undo.ts
|
|
1120
|
+
init_errors();
|
|
930
1121
|
async function undo(cwd) {
|
|
931
1122
|
const entry = await readUndoEntry(cwd);
|
|
932
1123
|
if (!await isWorkingTreeClean(cwd)) {
|
|
@@ -974,6 +1165,7 @@ async function undo(cwd) {
|
|
|
974
1165
|
}
|
|
975
1166
|
|
|
976
1167
|
// src/index.ts
|
|
1168
|
+
init_errors();
|
|
977
1169
|
var require2 = createRequire(import.meta.url);
|
|
978
1170
|
var { version } = require2("../package.json");
|
|
979
1171
|
var program = new Command();
|
|
@@ -986,9 +1178,9 @@ Examples:
|
|
|
986
1178
|
).action(async () => {
|
|
987
1179
|
const result = await init(process.cwd());
|
|
988
1180
|
if (result.status === "created") {
|
|
989
|
-
console.log(
|
|
1181
|
+
console.log(chalk2.green("\u2714 DubStack initialized"));
|
|
990
1182
|
} else {
|
|
991
|
-
console.log(
|
|
1183
|
+
console.log(chalk2.yellow("\u26A0 DubStack already initialized"));
|
|
992
1184
|
}
|
|
993
1185
|
});
|
|
994
1186
|
program.command("create").argument("<branch-name>", "Name of the new branch to create").description("Create a new branch stacked on top of the current branch").option("-m, --message <message>", "Commit staged changes with this message").option("-a, --all", "Stage all changes before committing (requires -m)").addHelpText(
|
|
@@ -1006,13 +1198,13 @@ Examples:
|
|
|
1006
1198
|
});
|
|
1007
1199
|
if (result.committed) {
|
|
1008
1200
|
console.log(
|
|
1009
|
-
|
|
1201
|
+
chalk2.green(
|
|
1010
1202
|
`\u2714 Created '${result.branch}' on '${result.parent}' \u2022 ${result.committed}`
|
|
1011
1203
|
)
|
|
1012
1204
|
);
|
|
1013
1205
|
} else {
|
|
1014
1206
|
console.log(
|
|
1015
|
-
|
|
1207
|
+
chalk2.green(
|
|
1016
1208
|
`\u2714 Created branch '${result.branch}' on top of '${result.parent}'`
|
|
1017
1209
|
)
|
|
1018
1210
|
);
|
|
@@ -1026,7 +1218,7 @@ Examples:
|
|
|
1026
1218
|
$ dub log Show the branch tree with current branch highlighted`
|
|
1027
1219
|
).action(async () => {
|
|
1028
1220
|
const output = await log(process.cwd());
|
|
1029
|
-
const styled = output.replace(/\*(.+?) \(Current\)\*/g,
|
|
1221
|
+
const styled = output.replace(/\*(.+?) \(Current\)\*/g, chalk2.bold.cyan("$1 (Current)")).replace(/⚠ \(missing\)/g, chalk2.yellow("\u26A0 (missing)"));
|
|
1030
1222
|
console.log(styled);
|
|
1031
1223
|
});
|
|
1032
1224
|
program.command("restack").description("Rebase all branches in the stack onto their updated parents").option("--continue", "Continue restacking after resolving conflicts").addHelpText(
|
|
@@ -1038,22 +1230,22 @@ Examples:
|
|
|
1038
1230
|
).action(async (options) => {
|
|
1039
1231
|
const result = options.continue ? await restackContinue(process.cwd()) : await restack(process.cwd());
|
|
1040
1232
|
if (result.status === "up-to-date") {
|
|
1041
|
-
console.log(
|
|
1233
|
+
console.log(chalk2.green("\u2714 Stack is already up to date"));
|
|
1042
1234
|
} else if (result.status === "conflict") {
|
|
1043
1235
|
console.log(
|
|
1044
|
-
|
|
1236
|
+
chalk2.yellow(`\u26A0 Conflict while restacking '${result.conflictBranch}'`)
|
|
1045
1237
|
);
|
|
1046
1238
|
console.log(
|
|
1047
|
-
|
|
1239
|
+
chalk2.dim(
|
|
1048
1240
|
" Resolve conflicts, stage changes, then run: dub restack --continue"
|
|
1049
1241
|
)
|
|
1050
1242
|
);
|
|
1051
1243
|
} else {
|
|
1052
1244
|
console.log(
|
|
1053
|
-
|
|
1245
|
+
chalk2.green(`\u2714 Restacked ${result.rebased.length} branch(es)`)
|
|
1054
1246
|
);
|
|
1055
1247
|
for (const branch of result.rebased) {
|
|
1056
|
-
console.log(
|
|
1248
|
+
console.log(chalk2.dim(` \u21B3 ${branch}`));
|
|
1057
1249
|
}
|
|
1058
1250
|
}
|
|
1059
1251
|
});
|
|
@@ -1064,7 +1256,7 @@ Examples:
|
|
|
1064
1256
|
$ dub undo Roll back the last dub operation`
|
|
1065
1257
|
).action(async () => {
|
|
1066
1258
|
const result = await undo(process.cwd());
|
|
1067
|
-
console.log(
|
|
1259
|
+
console.log(chalk2.green(`\u2714 Undid '${result.undone}': ${result.details}`));
|
|
1068
1260
|
});
|
|
1069
1261
|
program.command("submit").description(
|
|
1070
1262
|
"Push branches and create/update GitHub PRs for the current stack"
|
|
@@ -1076,16 +1268,38 @@ Examples:
|
|
|
1076
1268
|
$ dub submit --dry-run Preview what would happen`
|
|
1077
1269
|
).action(runSubmit);
|
|
1078
1270
|
program.command("ss").description("Submit the current stack (alias for submit)").option("--dry-run", "Print what would happen without executing").action(runSubmit);
|
|
1271
|
+
program.command("co").argument("[branch]", "Branch to checkout (interactive if omitted)").description("Checkout a branch (interactive picker if no name given)").action(async (branch) => {
|
|
1272
|
+
if (branch) {
|
|
1273
|
+
const result = await checkout(branch, process.cwd());
|
|
1274
|
+
console.log(chalk2.green(`\u2714 Switched to '${result.branch}'`));
|
|
1275
|
+
} else {
|
|
1276
|
+
const result = await interactiveCheckout(process.cwd());
|
|
1277
|
+
if (result) {
|
|
1278
|
+
console.log(chalk2.green(`\u2714 Switched to '${result.branch}'`));
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
program.command("skills").description("Manage DubStack agent skills").addCommand(
|
|
1283
|
+
new Command("add").description("Install agent skills (e.g. dubstack, dub-flow)").argument("[skills...]", "Names of skills to install (default: all)").option("-g, --global", "Install skills globally").option("--dry-run", "Preview actions without installing").action(async (skills, options) => {
|
|
1284
|
+
const { addSkills: addSkills2 } = await Promise.resolve().then(() => (init_skills2(), skills_exports));
|
|
1285
|
+
await addSkills2(skills, options);
|
|
1286
|
+
})
|
|
1287
|
+
).addCommand(
|
|
1288
|
+
new Command("remove").description("Remove agent skills").argument("[skills...]", "Names of skills to remove (default: all)").option("-g, --global", "Remove skills globally").option("--dry-run", "Preview actions without removing").action(async (skills, options) => {
|
|
1289
|
+
const { removeSkills: removeSkills2 } = await Promise.resolve().then(() => (init_skills2(), skills_exports));
|
|
1290
|
+
await removeSkills2(skills, options);
|
|
1291
|
+
})
|
|
1292
|
+
);
|
|
1079
1293
|
async function runSubmit(options) {
|
|
1080
1294
|
const result = await submit(process.cwd(), options.dryRun ?? false);
|
|
1081
1295
|
if (result.pushed.length > 0) {
|
|
1082
1296
|
console.log(
|
|
1083
|
-
|
|
1297
|
+
chalk2.green(
|
|
1084
1298
|
`\u2714 Pushed ${result.pushed.length} branch(es), created ${result.created.length} PR(s), updated ${result.updated.length} PR(s)`
|
|
1085
1299
|
)
|
|
1086
1300
|
);
|
|
1087
1301
|
for (const branch of [...result.created, ...result.updated]) {
|
|
1088
|
-
console.log(
|
|
1302
|
+
console.log(chalk2.dim(` \u21B3 ${branch}`));
|
|
1089
1303
|
}
|
|
1090
1304
|
}
|
|
1091
1305
|
}
|
|
@@ -1094,7 +1308,7 @@ async function main() {
|
|
|
1094
1308
|
await program.parseAsync(process.argv);
|
|
1095
1309
|
} catch (error) {
|
|
1096
1310
|
if (error instanceof DubError) {
|
|
1097
|
-
console.error(
|
|
1311
|
+
console.error(chalk2.red(`\u2716 ${error.message}`));
|
|
1098
1312
|
process.exit(1);
|
|
1099
1313
|
}
|
|
1100
1314
|
throw error;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lib/errors.ts","../src/lib/git.ts","../src/lib/state.ts","../src/lib/undo-log.ts","../src/commands/create.ts","../src/commands/init.ts","../src/commands/log.ts","../src/commands/restack.ts","../src/commands/submit.ts","../src/lib/github.ts","../src/lib/pr-body.ts","../src/commands/undo.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * DubStack CLI — manage stacked diffs with ease.\n *\n * A local-first tool for managing chains of dependent git branches\n * (stacked diffs) without manually dealing with complex rebase chains.\n *\n * @example\n * ```bash\n * dub init # Initialize in current repo\n * dub create feat/my-branch # Create stacked branch\n * dub log # View stack tree\n * dub restack # Rebase stack onto updated parent\n * dub undo # Undo last dub operation\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createRequire } from \"node:module\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { create } from \"./commands/create\";\nimport { init } from \"./commands/init\";\nimport { log } from \"./commands/log\";\nimport { restack, restackContinue } from \"./commands/restack\";\nimport { submit } from \"./commands/submit\";\nimport { undo } from \"./commands/undo\";\nimport { DubError } from \"./lib/errors\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n\t.name(\"dub\")\n\t.description(\"Manage stacked diffs (dependent git branches) with ease\")\n\t.version(version);\n\nprogram\n\t.command(\"init\")\n\t.description(\"Initialize DubStack in the current git repository\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub init Initialize DubStack, creating .git/dubstack/ and updating .gitignore`,\n\t)\n\t.action(async () => {\n\t\tconst result = await init(process.cwd());\n\t\tif (result.status === \"created\") {\n\t\t\tconsole.log(chalk.green(\"✔ DubStack initialized\"));\n\t\t} else {\n\t\t\tconsole.log(chalk.yellow(\"⚠ DubStack already initialized\"));\n\t\t}\n\t});\n\nprogram\n\t.command(\"create\")\n\t.argument(\"<branch-name>\", \"Name of the new branch to create\")\n\t.description(\"Create a new branch stacked on top of the current branch\")\n\t.option(\"-m, --message <message>\", \"Commit staged changes with this message\")\n\t.option(\"-a, --all\", \"Stage all changes before committing (requires -m)\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub create feat/api Create branch only\n $ dub create feat/api -m \"feat: add API\" Create branch + commit staged\n $ dub create feat/api -am \"feat: add API\" Stage all + create + commit`,\n\t)\n\t.action(\n\t\tasync (\n\t\t\tbranchName: string,\n\t\t\toptions: { message?: string; all?: boolean },\n\t\t) => {\n\t\t\tconst result = await create(branchName, process.cwd(), {\n\t\t\t\tmessage: options.message,\n\t\t\t\tall: options.all,\n\t\t\t});\n\t\t\tif (result.committed) {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green(\n\t\t\t\t\t\t`✔ Created '${result.branch}' on '${result.parent}' • ${result.committed}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green(\n\t\t\t\t\t\t`✔ Created branch '${result.branch}' on top of '${result.parent}'`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command(\"log\")\n\t.description(\"Display an ASCII tree of the current stack\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub log Show the branch tree with current branch highlighted`,\n\t)\n\t.action(async () => {\n\t\tconst output = await log(process.cwd());\n\t\t// Apply chalk styling to the output\n\t\tconst styled = output\n\t\t\t.replace(/\\*(.+?) \\(Current\\)\\*/g, chalk.bold.cyan(\"$1 (Current)\"))\n\t\t\t.replace(/⚠ \\(missing\\)/g, chalk.yellow(\"⚠ (missing)\"));\n\t\tconsole.log(styled);\n\t});\n\nprogram\n\t.command(\"restack\")\n\t.description(\"Rebase all branches in the stack onto their updated parents\")\n\t.option(\"--continue\", \"Continue restacking after resolving conflicts\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub restack Rebase the current stack\n $ dub restack --continue Continue after resolving conflicts`,\n\t)\n\t.action(async (options: { continue?: boolean }) => {\n\t\tconst result = options.continue\n\t\t\t? await restackContinue(process.cwd())\n\t\t\t: await restack(process.cwd());\n\n\t\tif (result.status === \"up-to-date\") {\n\t\t\tconsole.log(chalk.green(\"✔ Stack is already up to date\"));\n\t\t} else if (result.status === \"conflict\") {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(`⚠ Conflict while restacking '${result.conflictBranch}'`),\n\t\t\t);\n\t\t\tconsole.log(\n\t\t\t\tchalk.dim(\n\t\t\t\t\t\" Resolve conflicts, stage changes, then run: dub restack --continue\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.log(\n\t\t\t\tchalk.green(`✔ Restacked ${result.rebased.length} branch(es)`),\n\t\t\t);\n\t\t\tfor (const branch of result.rebased) {\n\t\t\t\tconsole.log(chalk.dim(` ↳ ${branch}`));\n\t\t\t}\n\t\t}\n\t});\n\nprogram\n\t.command(\"undo\")\n\t.description(\"Undo the last dub create or dub restack operation\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub undo Roll back the last dub operation`,\n\t)\n\t.action(async () => {\n\t\tconst result = await undo(process.cwd());\n\t\tconsole.log(chalk.green(`✔ Undid '${result.undone}': ${result.details}`));\n\t});\n\nprogram\n\t.command(\"submit\")\n\t.description(\n\t\t\"Push branches and create/update GitHub PRs for the current stack\",\n\t)\n\t.option(\"--dry-run\", \"Print what would happen without executing\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub submit Push and create/update PRs\n $ dub submit --dry-run Preview what would happen`,\n\t)\n\t.action(runSubmit);\n\nprogram\n\t.command(\"ss\")\n\t.description(\"Submit the current stack (alias for submit)\")\n\t.option(\"--dry-run\", \"Print what would happen without executing\")\n\t.action(runSubmit);\n\nasync function runSubmit(options: { dryRun?: boolean }) {\n\tconst result = await submit(process.cwd(), options.dryRun ?? false);\n\n\tif (result.pushed.length > 0) {\n\t\tconsole.log(\n\t\t\tchalk.green(\n\t\t\t\t`✔ Pushed ${result.pushed.length} branch(es), created ${result.created.length} PR(s), updated ${result.updated.length} PR(s)`,\n\t\t\t),\n\t\t);\n\t\tfor (const branch of [...result.created, ...result.updated]) {\n\t\t\tconsole.log(chalk.dim(` ↳ ${branch}`));\n\t\t}\n\t}\n}\n\nasync function main() {\n\ttry {\n\t\tawait program.parseAsync(process.argv);\n\t} catch (error) {\n\t\tif (error instanceof DubError) {\n\t\t\tconsole.error(chalk.red(`✖ ${error.message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nmain();\n","/**\n * Base error class for all user-facing DubStack errors.\n *\n * The CLI entry point catches instances of this class and prints\n * a clean, colored error message. Unknown errors get a full stack trace.\n *\n * @example\n * ```ts\n * throw new DubError(\"Branch 'feat/x' already exists\")\n * ```\n */\nexport class DubError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"DubError\";\n\t}\n}\n","import { execa } from \"execa\";\nimport { DubError } from \"./errors\";\n\n/**\n * Checks whether the given directory is inside a git repository.\n * @returns `true` if inside a git worktree, `false` otherwise. Never throws.\n */\nexport async function isGitRepo(cwd: string): Promise<boolean> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"rev-parse\", \"--is-inside-work-tree\"],\n\t\t\t{ cwd },\n\t\t);\n\t\treturn stdout.trim() === \"true\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Returns the absolute path to the repository root.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getRepoRoot(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n\t\t\tcwd,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"Not a git repository. Run this command inside a git repo.\",\n\t\t);\n\t}\n}\n\n/**\n * Returns the name of the currently checked-out branch.\n * @throws {DubError} If HEAD is detached or the repo has no commits.\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"rev-parse\", \"--abbrev-ref\", \"HEAD\"],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst branch = stdout.trim();\n\t\tif (branch === \"HEAD\") {\n\t\t\tthrow new DubError(\n\t\t\t\t\"HEAD is detached. Checkout a branch before running this command.\",\n\t\t\t);\n\t\t}\n\t\treturn branch;\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(\n\t\t\t\"Repository has no commits. Make at least one commit first.\",\n\t\t);\n\t}\n}\n\n/**\n * Checks whether a branch with the given name exists locally.\n * @returns `true` if the branch exists, `false` otherwise. Never throws.\n */\nexport async function branchExists(\n\tname: string,\n\tcwd: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"git\", [\"rev-parse\", \"--verify\", `refs/heads/${name}`], {\n\t\t\tcwd,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Creates a new branch and switches to it.\n * @throws {DubError} If a branch with that name already exists.\n */\nexport async function createBranch(name: string, cwd: string): Promise<void> {\n\tif (await branchExists(name, cwd)) {\n\t\tthrow new DubError(`Branch '${name}' already exists.`);\n\t}\n\tawait execa(\"git\", [\"checkout\", \"-b\", name], { cwd });\n}\n\n/**\n * Switches to an existing branch.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function checkoutBranch(name: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"checkout\", name], { cwd });\n\t} catch {\n\t\tthrow new DubError(`Branch '${name}' not found.`);\n\t}\n}\n\n/**\n * Deletes a local branch forcefully. Used by undo to remove created branches.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function deleteBranch(name: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"branch\", \"-D\", name], { cwd });\n\t} catch {\n\t\tthrow new DubError(`Failed to delete branch '${name}'. It may not exist.`);\n\t}\n}\n\n/**\n * Force-moves a branch pointer to a specific commit SHA.\n * Used by undo to reset branches to their pre-operation tips.\n */\nexport async function forceBranchTo(\n\tname: string,\n\tsha: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tconst current = await getCurrentBranch(cwd).catch(() => null);\n\t\tif (current === name) {\n\t\t\tawait execa(\"git\", [\"reset\", \"--hard\", sha], { cwd });\n\t\t} else {\n\t\t\tawait execa(\"git\", [\"branch\", \"-f\", name, sha], { cwd });\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(`Failed to reset branch '${name}' to ${sha}.`);\n\t}\n}\n\n/**\n * Checks whether the working tree is clean (no uncommitted changes).\n * @returns `true` if clean (no output from `git status --porcelain`).\n */\nexport async function isWorkingTreeClean(cwd: string): Promise<boolean> {\n\tconst { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"], { cwd });\n\treturn stdout.trim() === \"\";\n}\n\n/**\n * Performs `git rebase --onto` to move a branch from one base to another.\n *\n * @param newBase - The commit/branch to rebase onto\n * @param oldBase - The old base commit to replay from\n * @param branch - The branch being rebased\n * @throws {DubError} If a merge conflict occurs during rebase\n */\nexport async function rebaseOnto(\n\tnewBase: string,\n\toldBase: string,\n\tbranch: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"rebase\", \"--onto\", newBase, oldBase, branch], { cwd });\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Conflict while restacking '${branch}'.\\n` +\n\t\t\t\t\" Resolve conflicts, stage changes, then run: dub restack --continue\",\n\t\t);\n\t}\n}\n\n/**\n * Continues an in-progress rebase after conflicts have been resolved.\n * @throws {DubError} If the rebase continue fails.\n */\nexport async function rebaseContinue(cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"rebase\", \"--continue\"], {\n\t\t\tcwd,\n\t\t\tenv: { GIT_EDITOR: \"true\" },\n\t\t});\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"Failed to continue rebase. Ensure all conflicts are resolved and staged.\",\n\t\t);\n\t}\n}\n\n/**\n * Returns the merge-base (common ancestor) commit SHA of two branches.\n */\nexport async function getMergeBase(\n\ta: string,\n\tb: string,\n\tcwd: string,\n): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"merge-base\", a, b], { cwd });\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Could not find common ancestor between '${a}' and '${b}'.`,\n\t\t);\n\t}\n}\n\n/**\n * Returns the commit SHA at the tip of a branch.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function getBranchTip(name: string, cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"rev-parse\", name], { cwd });\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(`Branch '${name}' not found.`);\n\t}\n}\n\n/**\n * Returns the subject line of the most recent commit on a branch.\n * @throws {DubError} If the branch has no commits.\n */\nexport async function getLastCommitMessage(\n\tbranch: string,\n\tcwd: string,\n): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"log\", \"-1\", \"--format=%s\", branch],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst message = stdout.trim();\n\t\tif (!message) {\n\t\t\tthrow new DubError(`Branch '${branch}' has no commits.`);\n\t\t}\n\t\treturn message;\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(`Failed to read commit message for '${branch}'.`);\n\t}\n}\n\n/**\n * Pushes a branch to origin with `--force-with-lease`.\n * @throws {DubError} If the push fails.\n */\nexport async function pushBranch(branch: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"push\", \"--force-with-lease\", \"origin\", branch], {\n\t\t\tcwd,\n\t\t});\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Failed to push '${branch}'. The remote ref may have been updated by someone else.`,\n\t\t);\n\t}\n}\n\n/**\n * Stages all changes (tracked, untracked, and deletions).\n * @throws {DubError} If git add fails.\n */\nexport async function stageAll(cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"add\", \"-A\"], { cwd });\n\t} catch {\n\t\tthrow new DubError(\"Failed to stage changes.\");\n\t}\n}\n\n/**\n * Checks whether there are staged changes ready to commit.\n *\n * `git diff --cached --quiet` exits with code 1 when changes exist\n * and code 0 when there are none.\n */\nexport async function hasStagedChanges(cwd: string): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"git\", [\"diff\", \"--cached\", \"--quiet\"], { cwd });\n\t\treturn false;\n\t} catch (error: unknown) {\n\t\tconst exitCode = (error as { exitCode?: number }).exitCode;\n\t\tif (exitCode === 1) return true;\n\t\tthrow new DubError(\"Failed to check staged changes.\");\n\t}\n}\n\n/**\n * Commits currently staged changes with the given message.\n * @throws {DubError} If the commit fails (e.g., nothing staged, hook rejection).\n */\nexport async function commitStaged(\n\tmessage: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"commit\", \"-m\", message], { cwd });\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Commit failed. Ensure there are staged changes and git hooks pass.`,\n\t\t);\n\t}\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"./errors\";\nimport { getRepoRoot } from \"./git\";\n\n/** A branch within a stack. */\nexport interface Branch {\n\t/** Branch name, e.g. \"feat/api-endpoint\" */\n\tname: string;\n\t/** Set to \"root\" for the base branch (e.g. main). Omitted for children. */\n\ttype?: \"root\";\n\t/** Name of the parent branch. `null` only for root branches. */\n\tparent: string | null;\n\t/** GitHub PR number. Populated after `dub submit`. */\n\tpr_number: number | null;\n\t/** GitHub PR URL. Populated after `dub submit`. */\n\tpr_link: string | null;\n}\n\n/** A stack of dependent branches. */\nexport interface Stack {\n\t/** Unique identifier for this stack. */\n\tid: string;\n\t/** Ordered list of branches in the stack. */\n\tbranches: Branch[];\n}\n\n/** Root state persisted to `.git/dubstack/state.json`. */\nexport interface DubState {\n\t/** All tracked stacks in this repository. */\n\tstacks: Stack[];\n}\n\n/**\n * Returns the absolute path to the dubstack state file.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getStatePath(cwd: string): Promise<string> {\n\tconst root = await getRepoRoot(cwd);\n\treturn path.join(root, \".git\", \"dubstack\", \"state.json\");\n}\n\n/**\n * Returns the absolute path to the dubstack directory inside `.git`.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getDubDir(cwd: string): Promise<string> {\n\tconst root = await getRepoRoot(cwd);\n\treturn path.join(root, \".git\", \"dubstack\");\n}\n\n/**\n * Reads and parses the dubstack state file.\n * @throws {DubError} If the state file is missing or contains invalid JSON.\n */\nexport async function readState(cwd: string): Promise<DubState> {\n\tconst statePath = await getStatePath(cwd);\n\tif (!fs.existsSync(statePath)) {\n\t\tthrow new DubError(\"DubStack is not initialized. Run 'dub init' first.\");\n\t}\n\ttry {\n\t\tconst raw = fs.readFileSync(statePath, \"utf-8\");\n\t\treturn JSON.parse(raw) as DubState;\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"State file is corrupted. Delete .git/dubstack and run 'dub init' to re-initialize.\",\n\t\t);\n\t}\n}\n\n/**\n * Writes the dubstack state to disk.\n * Creates the parent directory if it doesn't exist.\n */\nexport async function writeState(state: DubState, cwd: string): Promise<void> {\n\tconst statePath = await getStatePath(cwd);\n\tconst dir = path.dirname(statePath);\n\tif (!fs.existsSync(dir)) {\n\t\tfs.mkdirSync(dir, { recursive: true });\n\t}\n\tfs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\n/**\n * Initializes the dubstack state directory and file.\n * Idempotent — returns `\"already_exists\"` if already initialized.\n *\n * @returns `\"created\"` if freshly initialized, `\"already_exists\"` if state file already present.\n */\nexport async function initState(\n\tcwd: string,\n): Promise<\"created\" | \"already_exists\"> {\n\tconst statePath = await getStatePath(cwd);\n\tconst dir = path.dirname(statePath);\n\n\tif (fs.existsSync(statePath)) {\n\t\treturn \"already_exists\";\n\t}\n\n\tfs.mkdirSync(dir, { recursive: true });\n\tconst emptyState: DubState = { stacks: [] };\n\tfs.writeFileSync(statePath, `${JSON.stringify(emptyState, null, 2)}\\n`);\n\treturn \"created\";\n}\n\n/**\n * Returns state, auto-initializing if not yet set up.\n * Only catches the \"not initialized\" error — corrupt state still throws.\n */\nexport async function ensureState(cwd: string): Promise<DubState> {\n\ttry {\n\t\treturn await readState(cwd);\n\t} catch (error) {\n\t\tif (\n\t\t\terror instanceof DubError &&\n\t\t\terror.message.includes(\"not initialized\")\n\t\t) {\n\t\t\tawait initState(cwd);\n\t\t\treturn await readState(cwd);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Finds the stack containing a given branch.\n * @returns The matching stack, or `undefined` if the branch isn't tracked.\n */\nexport function findStackForBranch(\n\tstate: DubState,\n\tname: string,\n): Stack | undefined {\n\treturn state.stacks.find((stack) =>\n\t\tstack.branches.some((b) => b.name === name),\n\t);\n}\n\n/**\n * Adds a child branch to the state, linking it to its parent.\n *\n * Decision tree:\n * 1. If `child` already exists in any stack → throws `DubError` (no duplicates)\n * 2. If `parent` is found in an existing stack → appends child to that stack\n * 3. If `parent` is not in any stack → creates a new stack with parent as root\n *\n * @param state - The state to mutate (modified in place)\n * @param child - Name of the new branch\n * @param parent - Name of the parent branch\n * @throws {DubError} If child branch already exists in state\n */\nexport function addBranchToStack(\n\tstate: DubState,\n\tchild: string,\n\tparent: string,\n): void {\n\tif (findStackForBranch(state, child)) {\n\t\tthrow new DubError(`Branch '${child}' is already tracked in a stack.`);\n\t}\n\n\tconst childBranch: Branch = {\n\t\tname: child,\n\t\tparent,\n\t\tpr_number: null,\n\t\tpr_link: null,\n\t};\n\tconst existingStack = findStackForBranch(state, parent);\n\n\tif (existingStack) {\n\t\texistingStack.branches.push(childBranch);\n\t} else {\n\t\tconst rootBranch: Branch = {\n\t\t\tname: parent,\n\t\t\ttype: \"root\",\n\t\t\tparent: null,\n\t\t\tpr_number: null,\n\t\t\tpr_link: null,\n\t\t};\n\t\tstate.stacks.push({\n\t\t\tid: crypto.randomUUID(),\n\t\t\tbranches: [rootBranch, childBranch],\n\t\t});\n\t}\n}\n\n/**\n * Returns branches in topological (BFS) order starting from the root.\n * Useful for operations that need to process parent branches before children.\n */\nexport function topologicalOrder(stack: Stack): Branch[] {\n\tconst result: Branch[] = [];\n\tconst root = stack.branches.find((b) => b.type === \"root\");\n\tif (!root) return result;\n\n\tconst childMap = new Map<string, Branch[]>();\n\tfor (const branch of stack.branches) {\n\t\tif (branch.parent) {\n\t\t\tconst children = childMap.get(branch.parent) ?? [];\n\t\t\tchildren.push(branch);\n\t\t\tchildMap.set(branch.parent, children);\n\t\t}\n\t}\n\n\tconst queue = [root];\n\twhile (queue.length > 0) {\n\t\tconst current = queue.shift();\n\t\tif (!current) break;\n\t\tresult.push(current);\n\t\tconst children = childMap.get(current.name) ?? [];\n\t\tqueue.push(...children);\n\t}\n\n\treturn result;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"./errors\";\nimport type { DubState } from \"./state\";\nimport { getDubDir } from \"./state\";\n\n/**\n * Snapshot of system state before a mutation, used by `dub undo`.\n * Only one undo level is supported — each new mutation overwrites the previous snapshot.\n */\nexport interface UndoEntry {\n\t/** Which command created this snapshot. */\n\toperation: \"create\" | \"restack\";\n\t/** ISO timestamp of when the snapshot was taken. */\n\ttimestamp: string;\n\t/** The branch user was on before the operation. */\n\tpreviousBranch: string;\n\t/** Full copy of state.json before mutation. */\n\tpreviousState: DubState;\n\t/** Map of branch name → commit SHA before mutation. */\n\tbranchTips: Record<string, string>;\n\t/** Branches created by this operation (to be deleted on undo). */\n\tcreatedBranches: string[];\n}\n\nasync function getUndoPath(cwd: string): Promise<string> {\n\tconst dubDir = await getDubDir(cwd);\n\treturn path.join(dubDir, \"undo.json\");\n}\n\n/**\n * Saves an undo entry to disk. Overwrites any previous entry (1 level only).\n */\nexport async function saveUndoEntry(\n\tentry: UndoEntry,\n\tcwd: string,\n): Promise<void> {\n\tconst undoPath = await getUndoPath(cwd);\n\tfs.writeFileSync(undoPath, `${JSON.stringify(entry, null, 2)}\\n`);\n}\n\n/**\n * Reads the most recent undo entry.\n * @throws {DubError} If no undo entry exists.\n */\nexport async function readUndoEntry(cwd: string): Promise<UndoEntry> {\n\tconst undoPath = await getUndoPath(cwd);\n\tif (!fs.existsSync(undoPath)) {\n\t\tthrow new DubError(\"Nothing to undo.\");\n\t}\n\tconst raw = fs.readFileSync(undoPath, \"utf-8\");\n\treturn JSON.parse(raw) as UndoEntry;\n}\n\n/**\n * Deletes the undo entry file. Called after a successful undo.\n */\nexport async function clearUndoEntry(cwd: string): Promise<void> {\n\tconst undoPath = await getUndoPath(cwd);\n\tif (fs.existsSync(undoPath)) {\n\t\tfs.unlinkSync(undoPath);\n\t}\n}\n","import { DubError } from \"../lib/errors\";\nimport {\n\tbranchExists,\n\tcommitStaged,\n\tcreateBranch,\n\tgetCurrentBranch,\n\thasStagedChanges,\n\tstageAll,\n} from \"../lib/git\";\nimport { addBranchToStack, ensureState, writeState } from \"../lib/state\";\nimport { saveUndoEntry } from \"../lib/undo-log\";\n\ninterface CreateOptions {\n\tmessage?: string;\n\tall?: boolean;\n}\n\ninterface CreateResult {\n\tbranch: string;\n\tparent: string;\n\tcommitted?: string;\n}\n\n/**\n * Creates a new branch stacked on top of the current branch.\n *\n * When `-m` is provided, also commits staged changes on the new branch.\n * When `-a` is provided, stages all changes first (requires `-m`).\n *\n * @param name - Name of the new branch to create\n * @param cwd - Working directory (auto-initializes if needed)\n * @param options - Optional message and all flags\n * @returns The created branch name, its parent, and committed message if applicable\n * @throws {DubError} If branch exists, HEAD is detached, -a without -m, or nothing to commit\n */\nexport async function create(\n\tname: string,\n\tcwd: string,\n\toptions?: CreateOptions,\n): Promise<CreateResult> {\n\tif (options?.all && !options.message) {\n\t\tthrow new DubError(\"'-a' requires '-m'. Pass a commit message.\");\n\t}\n\n\tconst state = await ensureState(cwd);\n\tconst parent = await getCurrentBranch(cwd);\n\n\tif (await branchExists(name, cwd)) {\n\t\tthrow new DubError(`Branch '${name}' already exists.`);\n\t}\n\n\tif (options?.message) {\n\t\tif (options.all) {\n\t\t\tawait stageAll(cwd);\n\t\t}\n\n\t\tif (!(await hasStagedChanges(cwd))) {\n\t\t\tconst hint = options.all\n\t\t\t\t? \"No changes to commit.\"\n\t\t\t\t: \"No staged changes. Stage files with 'git add' or use '-a' to stage all.\";\n\t\t\tthrow new DubError(hint);\n\t\t}\n\t}\n\n\tawait saveUndoEntry(\n\t\t{\n\t\t\toperation: \"create\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpreviousBranch: parent,\n\t\t\tpreviousState: structuredClone(state),\n\t\t\tbranchTips: {},\n\t\t\tcreatedBranches: [name],\n\t\t},\n\t\tcwd,\n\t);\n\n\tawait createBranch(name, cwd);\n\taddBranchToStack(state, name, parent);\n\tawait writeState(state, cwd);\n\n\tif (options?.message) {\n\t\ttry {\n\t\t\tawait commitStaged(options.message, cwd);\n\t\t} catch (error) {\n\t\t\tconst reason = error instanceof DubError ? error.message : String(error);\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${name}' was created but commit failed: ${reason}. Run 'dub undo' to clean up.`,\n\t\t\t);\n\t\t}\n\t\treturn { branch: name, parent, committed: options.message };\n\t}\n\n\treturn { branch: name, parent };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport { getRepoRoot, isGitRepo } from \"../lib/git\";\nimport { initState } from \"../lib/state\";\n\ninterface InitResult {\n\tstatus: \"created\" | \"already_exists\";\n\tgitignoreUpdated: boolean;\n}\n\n/**\n * Initializes DubStack in the current git repository.\n *\n * Creates `.git/dubstack/state.json` with an empty state and ensures\n * `.git/dubstack` is listed in `.gitignore`. Idempotent — safe to run\n * multiple times.\n *\n * @param cwd - Working directory (must be inside a git repo)\n * @returns Status indicating whether state was created or already existed\n * @throws {DubError} If not inside a git repository\n */\nexport async function init(cwd: string): Promise<InitResult> {\n\tif (!(await isGitRepo(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Not a git repository. Run this command inside a git repo.\",\n\t\t);\n\t}\n\n\tconst status = await initState(cwd);\n\tconst repoRoot = await getRepoRoot(cwd);\n\tconst gitignorePath = path.join(repoRoot, \".gitignore\");\n\tconst entry = \".git/dubstack\";\n\tlet gitignoreUpdated = false;\n\n\tif (fs.existsSync(gitignorePath)) {\n\t\tconst content = fs.readFileSync(gitignorePath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\");\n\t\tif (!lines.some((line) => line.trim() === entry)) {\n\t\t\tconst separator = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n\t\t\tfs.writeFileSync(gitignorePath, `${content}${separator}${entry}\\n`);\n\t\t\tgitignoreUpdated = true;\n\t\t}\n\t} else {\n\t\tfs.writeFileSync(gitignorePath, `${entry}\\n`);\n\t\tgitignoreUpdated = true;\n\t}\n\n\treturn { status, gitignoreUpdated };\n}\n","import { branchExists, getCurrentBranch } from \"../lib/git\";\nimport type { Branch, Stack } from \"../lib/state\";\nimport { readState } from \"../lib/state\";\n\n/**\n * Renders an ASCII tree view of all tracked stacks.\n *\n * Highlights the current branch, marks branches missing from git,\n * and handles multiple stacks separated by blank lines.\n *\n * @param cwd - Working directory (must be inside an initialized dubstack repo)\n * @returns Formatted ASCII tree string (no ANSI colors — caller adds chalk)\n * @throws {DubError} If not initialized\n */\nexport async function log(cwd: string): Promise<string> {\n\tconst state = await readState(cwd);\n\n\tif (state.stacks.length === 0) {\n\t\treturn \"No stacks. Run 'dub create' to start.\";\n\t}\n\n\tlet currentBranch: string | null = null;\n\ttry {\n\t\tcurrentBranch = await getCurrentBranch(cwd);\n\t} catch {\n\t\t// Detached HEAD or empty repo — no branch highlighted\n\t}\n\n\tconst sections: string[] = [];\n\n\tfor (const stack of state.stacks) {\n\t\tconst tree = await renderStack(stack, currentBranch, cwd);\n\t\tsections.push(tree);\n\t}\n\n\treturn sections.join(\"\\n\\n\");\n}\n\nasync function renderStack(\n\tstack: Stack,\n\tcurrentBranch: string | null,\n\tcwd: string,\n): Promise<string> {\n\tconst root = stack.branches.find((b) => b.type === \"root\");\n\tif (!root) return \"\";\n\n\tconst childMap = new Map<string, Branch[]>();\n\tfor (const branch of stack.branches) {\n\t\tif (branch.parent) {\n\t\t\tconst children = childMap.get(branch.parent) ?? [];\n\t\t\tchildren.push(branch);\n\t\t\tchildMap.set(branch.parent, children);\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\tawait renderNode(root, currentBranch, childMap, \"\", true, true, lines, cwd);\n\treturn lines.join(\"\\n\");\n}\n\nasync function renderNode(\n\tbranch: Branch,\n\tcurrentBranch: string | null,\n\tchildMap: Map<string, Branch[]>,\n\tprefix: string,\n\tisRoot: boolean,\n\tisLast: boolean,\n\tlines: string[],\n\tcwd: string,\n): Promise<void> {\n\tlet label: string;\n\tconst exists = await branchExists(branch.name, cwd);\n\n\tif (isRoot) {\n\t\tlabel = `(${branch.name})`;\n\t} else if (branch.name === currentBranch) {\n\t\tlabel = `*${branch.name} (Current)*`;\n\t} else if (!exists) {\n\t\tlabel = `${branch.name} ⚠ (missing)`;\n\t} else {\n\t\tlabel = branch.name;\n\t}\n\n\tif (isRoot) {\n\t\tlines.push(label);\n\t} else {\n\t\tconst connector = isLast ? \"└─ \" : \"├─ \";\n\t\tlines.push(`${prefix}${connector}${label}`);\n\t}\n\n\tconst children = childMap.get(branch.name) ?? [];\n\tconst childPrefix = isRoot ? \" \" : `${prefix}${isLast ? \" \" : \"│ \"}`;\n\n\tfor (let i = 0; i < children.length; i++) {\n\t\tconst isChildLast = i === children.length - 1;\n\t\tawait renderNode(\n\t\t\tchildren[i],\n\t\t\tcurrentBranch,\n\t\t\tchildMap,\n\t\t\tchildPrefix,\n\t\t\tfalse,\n\t\t\tisChildLast,\n\t\t\tlines,\n\t\t\tcwd,\n\t\t);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport {\n\tbranchExists,\n\tcheckoutBranch,\n\tgetBranchTip,\n\tgetCurrentBranch,\n\tgetMergeBase,\n\trebaseContinue as gitRebaseContinue,\n\tisWorkingTreeClean,\n\trebaseOnto,\n} from \"../lib/git\";\nimport {\n\tgetDubDir,\n\treadState,\n\ttype Stack,\n\ttopologicalOrder,\n} from \"../lib/state\";\nimport { saveUndoEntry } from \"../lib/undo-log\";\n\ninterface RestackStep {\n\tbranch: string;\n\tparent: string;\n\tparentOldTip: string;\n\tstatus: \"pending\" | \"done\" | \"skipped\" | \"conflicted\";\n}\n\ninterface RestackProgress {\n\toriginalBranch: string;\n\tsteps: RestackStep[];\n}\n\ninterface RestackResult {\n\tstatus: \"success\" | \"conflict\" | \"up-to-date\";\n\trebased: string[];\n\tconflictBranch?: string;\n}\n\n/**\n * Rebases all branches in the current stack onto their updated parents.\n *\n * Uses a snapshot-before-rebase strategy: captures every branch's tip\n * BEFORE starting any rebases, then uses `git rebase --onto <parent_new_tip>\n * <parent_old_tip> <child>`. This prevents the duplication bug where a child\n * replays its parent's already-rebased commits.\n *\n * On conflict, writes progress to `restack-progress.json` so\n * `dub restack --continue` can resume.\n *\n * @param cwd - Working directory\n * @returns Result with status, list of rebased branches, and optional conflict branch\n * @throws {DubError} If not initialized, dirty tree, not in a stack, or branch missing\n */\nexport async function restack(cwd: string): Promise<RestackResult> {\n\tconst state = await readState(cwd);\n\n\tif (!(await isWorkingTreeClean(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Working tree has uncommitted changes. Commit or stash them before restacking.\",\n\t\t);\n\t}\n\n\tconst originalBranch = await getCurrentBranch(cwd);\n\tconst targetStacks = getTargetStacks(state.stacks, originalBranch);\n\n\tif (targetStacks.length === 0) {\n\t\tthrow new DubError(\n\t\t\t`Branch '${originalBranch}' is not part of any stack. Run 'dub create' first.`,\n\t\t);\n\t}\n\n\tconst allBranches = targetStacks.flatMap((s) => s.branches);\n\tfor (const branch of allBranches) {\n\t\tif (!(await branchExists(branch.name, cwd))) {\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${branch.name}' is tracked in state but no longer exists in git.\\n` +\n\t\t\t\t\t\" Remove it from the stack or recreate it before restacking.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t// Snapshot all branch tips BEFORE building steps or rebasing\n\tconst branchTips: Record<string, string> = {};\n\tfor (const branch of allBranches) {\n\t\tbranchTips[branch.name] = await getBranchTip(branch.name, cwd);\n\t}\n\n\tconst steps = await buildRestackSteps(targetStacks, cwd);\n\n\tif (steps.length === 0) {\n\t\treturn { status: \"up-to-date\", rebased: [] };\n\t}\n\n\tawait saveUndoEntry(\n\t\t{\n\t\t\toperation: \"restack\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpreviousBranch: originalBranch,\n\t\t\tpreviousState: structuredClone(state),\n\t\t\tbranchTips,\n\t\t\tcreatedBranches: [],\n\t\t},\n\t\tcwd,\n\t);\n\n\tconst progress: RestackProgress = { originalBranch, steps };\n\tawait writeProgress(progress, cwd);\n\n\treturn executeRestackSteps(progress, cwd);\n}\n\n/**\n * Continues a restack after conflict resolution.\n *\n * Reads the saved progress file, finishes the in-progress rebase,\n * then resumes with remaining branches.\n *\n * @param cwd - Working directory\n * @throws {DubError} If no restack is in progress\n */\nexport async function restackContinue(cwd: string): Promise<RestackResult> {\n\tconst progress = await readProgress(cwd);\n\n\tif (!progress) {\n\t\tthrow new DubError(\"No restack in progress. Run 'dub restack' to start.\");\n\t}\n\n\tawait gitRebaseContinue(cwd);\n\n\tconst conflictedStep = progress.steps.find((s) => s.status === \"conflicted\");\n\tif (conflictedStep) {\n\t\tconflictedStep.status = \"done\";\n\t}\n\n\treturn executeRestackSteps(progress, cwd);\n}\n\nasync function executeRestackSteps(\n\tprogress: RestackProgress,\n\tcwd: string,\n): Promise<RestackResult> {\n\tconst rebased: string[] = [];\n\n\tfor (const step of progress.steps) {\n\t\tif (step.status !== \"pending\") {\n\t\t\tif (step.status === \"done\") rebased.push(step.branch);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst parentNewTip = await getBranchTip(step.parent, cwd);\n\t\tif (parentNewTip === step.parentOldTip) {\n\t\t\tstep.status = \"skipped\";\n\t\t\tawait writeProgress(progress, cwd);\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tawait rebaseOnto(parentNewTip, step.parentOldTip, step.branch, cwd);\n\t\t\tstep.status = \"done\";\n\t\t\trebased.push(step.branch);\n\t\t\tawait writeProgress(progress, cwd);\n\t\t} catch (error) {\n\t\t\tif (error instanceof DubError && error.message.includes(\"Conflict\")) {\n\t\t\t\tstep.status = \"conflicted\";\n\t\t\t\tawait writeProgress(progress, cwd);\n\t\t\t\treturn { status: \"conflict\", rebased, conflictBranch: step.branch };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tawait clearProgress(cwd);\n\tawait checkoutBranch(progress.originalBranch, cwd);\n\n\tconst allSkipped = progress.steps.every(\n\t\t(s) => s.status === \"skipped\" || s.status === \"done\",\n\t);\n\treturn {\n\t\tstatus: rebased.length === 0 && allSkipped ? \"up-to-date\" : \"success\",\n\t\trebased,\n\t};\n}\n\nfunction getTargetStacks(stacks: Stack[], currentBranch: string): Stack[] {\n\t// If current branch is a root of any stacks, restack all of them\n\tconst rootStacks = stacks.filter((s) =>\n\t\ts.branches.some((b) => b.name === currentBranch && b.type === \"root\"),\n\t);\n\tif (rootStacks.length > 0) return rootStacks;\n\n\t// Otherwise, find the stack containing the current branch\n\tconst stack = stacks.find((s) =>\n\t\ts.branches.some((b) => b.name === currentBranch),\n\t);\n\treturn stack ? [stack] : [];\n}\n\nasync function buildRestackSteps(\n\tstacks: Stack[],\n\tcwd: string,\n): Promise<RestackStep[]> {\n\tconst steps: RestackStep[] = [];\n\n\tfor (const stack of stacks) {\n\t\tconst ordered = topologicalOrder(stack);\n\t\tfor (const branch of ordered) {\n\t\t\tif (branch.type === \"root\" || !branch.parent) continue;\n\t\t\tconst mergeBase = await getMergeBase(branch.parent, branch.name, cwd);\n\t\t\tsteps.push({\n\t\t\t\tbranch: branch.name,\n\t\t\t\tparent: branch.parent,\n\t\t\t\tparentOldTip: mergeBase,\n\t\t\t\tstatus: \"pending\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn steps;\n}\n\nasync function getProgressPath(cwd: string): Promise<string> {\n\tconst dubDir = await getDubDir(cwd);\n\treturn path.join(dubDir, \"restack-progress.json\");\n}\n\nasync function writeProgress(\n\tprogress: RestackProgress,\n\tcwd: string,\n): Promise<void> {\n\tconst progressPath = await getProgressPath(cwd);\n\tfs.writeFileSync(progressPath, `${JSON.stringify(progress, null, 2)}\\n`);\n}\n\nasync function readProgress(cwd: string): Promise<RestackProgress | null> {\n\tconst progressPath = await getProgressPath(cwd);\n\tif (!fs.existsSync(progressPath)) return null;\n\tconst raw = fs.readFileSync(progressPath, \"utf-8\");\n\treturn JSON.parse(raw) as RestackProgress;\n}\n\nasync function clearProgress(cwd: string): Promise<void> {\n\tconst progressPath = await getProgressPath(cwd);\n\tif (fs.existsSync(progressPath)) {\n\t\tfs.unlinkSync(progressPath);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport { getCurrentBranch, getLastCommitMessage, pushBranch } from \"../lib/git\";\nimport {\n\tcheckGhAuth,\n\tcreatePr,\n\tensureGhInstalled,\n\tgetPr,\n\ttype PrInfo,\n\tupdatePrBody,\n} from \"../lib/github\";\nimport {\n\tbuildMetadataBlock,\n\tbuildStackTable,\n\tcomposePrBody,\n} from \"../lib/pr-body\";\nimport {\n\ttype Branch,\n\tfindStackForBranch,\n\treadState,\n\ttopologicalOrder,\n\twriteState,\n} from \"../lib/state\";\n\ninterface SubmitResult {\n\tpushed: string[];\n\tcreated: string[];\n\tupdated: string[];\n}\n\n/**\n * Pushes branches in the current stack and creates/updates GitHub PRs.\n *\n * @param cwd - Working directory\n * @param dryRun - If true, prints what would happen without executing\n * @throws {DubError} If not in a stack, on root branch, stack is non-linear, or gh errors\n */\nexport async function submit(\n\tcwd: string,\n\tdryRun: boolean,\n): Promise<SubmitResult> {\n\tawait ensureGhInstalled();\n\tawait checkGhAuth();\n\n\tconst state = await readState(cwd);\n\tconst currentBranch = await getCurrentBranch(cwd);\n\tconst stack = findStackForBranch(state, currentBranch);\n\n\tif (!stack) {\n\t\tthrow new DubError(\n\t\t\t`Branch '${currentBranch}' is not part of any stack. Run 'dub create' first.`,\n\t\t);\n\t}\n\n\tconst ordered = topologicalOrder(stack);\n\tconst currentEntry = ordered.find((b) => b.name === currentBranch);\n\tif (currentEntry?.type === \"root\") {\n\t\tthrow new DubError(\n\t\t\t\"Cannot submit from a root branch. Checkout a stack branch first.\",\n\t\t);\n\t}\n\n\tconst nonRootBranches = ordered.filter((b) => b.type !== \"root\");\n\n\tvalidateLinearStack(ordered);\n\n\tconst result: SubmitResult = { pushed: [], created: [], updated: [] };\n\tconst prMap = new Map<string, PrInfo>();\n\n\tfor (const branch of nonRootBranches) {\n\t\tif (dryRun) {\n\t\t\tconsole.log(`[dry-run] would push ${branch.name}`);\n\t\t} else {\n\t\t\tawait pushBranch(branch.name, cwd);\n\t\t}\n\t\tresult.pushed.push(branch.name);\n\t}\n\n\tfor (const branch of nonRootBranches) {\n\t\tconst base = branch.parent as string;\n\n\t\tif (dryRun) {\n\t\t\tconsole.log(`[dry-run] would check/create PR: ${branch.name} → ${base}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst existing = await getPr(branch.name, cwd);\n\t\tif (existing) {\n\t\t\tprMap.set(branch.name, existing);\n\t\t\tresult.updated.push(branch.name);\n\t\t} else {\n\t\t\tconst title = await getLastCommitMessage(branch.name, cwd);\n\t\t\tconst tmpFile = writeTempBody(\"\");\n\t\t\ttry {\n\t\t\t\tconst created = await createPr(branch.name, base, title, tmpFile, cwd);\n\t\t\t\tprMap.set(branch.name, created);\n\t\t\t\tresult.created.push(branch.name);\n\t\t\t} finally {\n\t\t\t\tcleanupTempFile(tmpFile);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!dryRun) {\n\t\tawait updateAllPrBodies(nonRootBranches, prMap, stack.id, cwd);\n\n\t\tfor (const branch of nonRootBranches) {\n\t\t\tconst pr = prMap.get(branch.name);\n\t\t\tif (pr) {\n\t\t\t\tconst stateBranch = stack.branches.find((b) => b.name === branch.name);\n\t\t\t\tif (stateBranch) {\n\t\t\t\t\tstateBranch.pr_number = pr.number;\n\t\t\t\t\tstateBranch.pr_link = pr.url;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tawait writeState(state, cwd);\n\t}\n\n\treturn result;\n}\n\nfunction validateLinearStack(ordered: Branch[]): void {\n\tconst childCount = new Map<string, number>();\n\tfor (const branch of ordered) {\n\t\tif (branch.parent) {\n\t\t\tchildCount.set(branch.parent, (childCount.get(branch.parent) ?? 0) + 1);\n\t\t}\n\t}\n\tfor (const [parent, count] of childCount) {\n\t\tif (count > 1) {\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${parent}' has ${count} children. ` +\n\t\t\t\t\t\"Branching stacks are not supported by submit. \" +\n\t\t\t\t\t\"Ensure each branch has at most one child.\",\n\t\t\t);\n\t\t}\n\t}\n}\n\nasync function updateAllPrBodies(\n\tbranches: Branch[],\n\tprMap: Map<string, PrInfo>,\n\tstackId: string,\n\tcwd: string,\n): Promise<void> {\n\tconst tableEntries = new Map<string, { number: number; title: string }>();\n\tfor (const branch of branches) {\n\t\tconst pr = prMap.get(branch.name);\n\t\tif (pr) {\n\t\t\ttableEntries.set(branch.name, { number: pr.number, title: pr.title });\n\t\t}\n\t}\n\n\tfor (let i = 0; i < branches.length; i++) {\n\t\tconst branch = branches[i];\n\t\tconst pr = prMap.get(branch.name);\n\t\tif (!pr) continue;\n\n\t\tconst prevPr =\n\t\t\ti > 0 ? (prMap.get(branches[i - 1].name)?.number ?? null) : null;\n\t\tconst nextPr =\n\t\t\ti < branches.length - 1\n\t\t\t\t? (prMap.get(branches[i + 1].name)?.number ?? null)\n\t\t\t\t: null;\n\n\t\tconst stackTable = buildStackTable(branches, tableEntries, branch.name);\n\t\tconst metadataBlock = buildMetadataBlock(\n\t\t\tstackId,\n\t\t\tpr.number,\n\t\t\tprevPr,\n\t\t\tnextPr,\n\t\t\tbranch.name,\n\t\t);\n\n\t\tconst existingBody = pr.body;\n\t\tconst finalBody = composePrBody(existingBody, stackTable, metadataBlock);\n\n\t\tconst tmpFile = writeTempBody(finalBody);\n\t\ttry {\n\t\t\tawait updatePrBody(pr.number, tmpFile, cwd);\n\t\t} finally {\n\t\t\tcleanupTempFile(tmpFile);\n\t\t}\n\t}\n}\n\nfunction writeTempBody(content: string): string {\n\tconst tmpDir = os.tmpdir();\n\tconst tmpFile = path.join(tmpDir, `dubstack-body-${Date.now()}.md`);\n\tfs.writeFileSync(tmpFile, content);\n\treturn tmpFile;\n}\n\nfunction cleanupTempFile(filePath: string): void {\n\ttry {\n\t\tfs.unlinkSync(filePath);\n\t} catch {\n\t\t// Best-effort cleanup\n\t}\n}\n","import { execa } from \"execa\";\nimport { DubError } from \"./errors\";\n\n/** Details of a GitHub Pull Request. */\nexport interface PrInfo {\n\tnumber: number;\n\turl: string;\n\ttitle: string;\n\tbody: string;\n}\n\n/**\n * Ensures the `gh` CLI is installed and available in PATH.\n * @throws {DubError} If `gh` is not found.\n */\nexport async function ensureGhInstalled(): Promise<void> {\n\ttry {\n\t\tawait execa(\"gh\", [\"--version\"]);\n\t} catch {\n\t\tthrow new DubError(\"gh CLI not found. Install it: https://cli.github.com\");\n\t}\n}\n\n/**\n * Ensures the user is authenticated with `gh`.\n * @throws {DubError} If not authenticated.\n */\nexport async function checkGhAuth(): Promise<void> {\n\ttry {\n\t\tawait execa(\"gh\", [\"auth\", \"status\"]);\n\t} catch {\n\t\tthrow new DubError(\"Not authenticated with GitHub. Run 'gh auth login'.\");\n\t}\n}\n\n/**\n * Fetches the open PR for a given head branch, if one exists.\n * @returns The PR info, or `null` if no open PR exists for that branch.\n */\nexport async function getPr(\n\tbranch: string,\n\tcwd: string,\n): Promise<PrInfo | null> {\n\tconst { stdout } = await execa(\n\t\t\"gh\",\n\t\t[\n\t\t\t\"pr\",\n\t\t\t\"list\",\n\t\t\t\"--head\",\n\t\t\tbranch,\n\t\t\t\"--state\",\n\t\t\t\"open\",\n\t\t\t\"--json\",\n\t\t\t\"number,url,title,body\",\n\t\t\t\"--jq\",\n\t\t\t\".[0]\",\n\t\t],\n\t\t{ cwd },\n\t);\n\n\tconst trimmed = stdout.trim();\n\tif (!trimmed || trimmed === \"null\") return null;\n\n\ttry {\n\t\treturn JSON.parse(trimmed) as PrInfo;\n\t} catch {\n\t\tthrow new DubError(`Failed to parse PR info for branch '${branch}'.`);\n\t}\n}\n\n/**\n * Creates a new PR and returns its info.\n *\n * Parses the PR number from the URL printed to stdout by `gh pr create`,\n * avoiding an extra API round-trip.\n *\n * @param branch - Head branch\n * @param base - Base branch the PR merges into\n * @param title - PR title\n * @param bodyFile - Absolute path to a file containing the PR body\n */\nexport async function createPr(\n\tbranch: string,\n\tbase: string,\n\ttitle: string,\n\tbodyFile: string,\n\tcwd: string,\n): Promise<PrInfo> {\n\tlet stdout: string;\n\ttry {\n\t\tconst result = await execa(\n\t\t\t\"gh\",\n\t\t\t[\n\t\t\t\t\"pr\",\n\t\t\t\t\"create\",\n\t\t\t\t\"--head\",\n\t\t\t\tbranch,\n\t\t\t\t\"--base\",\n\t\t\t\tbase,\n\t\t\t\t\"--title\",\n\t\t\t\ttitle,\n\t\t\t\t\"--body-file\",\n\t\t\t\tbodyFile,\n\t\t\t],\n\t\t\t{ cwd },\n\t\t);\n\t\tstdout = result.stdout;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"403\") || message.includes(\"insufficient\")) {\n\t\t\tthrow new DubError(\n\t\t\t\t\"GitHub token lacks required permissions. Run 'gh auth login' with the 'repo' scope.\",\n\t\t\t);\n\t\t}\n\t\tthrow new DubError(`Failed to create PR for '${branch}': ${message}`);\n\t}\n\n\tconst url = stdout.trim();\n\tconst numberMatch = url.match(/\\/pull\\/(\\d+)$/);\n\tif (!numberMatch) {\n\t\tthrow new DubError(`Unexpected output from 'gh pr create': ${url}`);\n\t}\n\n\treturn {\n\t\tnumber: Number.parseInt(numberMatch[1], 10),\n\t\turl,\n\t\ttitle,\n\t\tbody: \"\",\n\t};\n}\n\n/**\n * Updates a PR's body using a file.\n * @param prNumber - The PR number to update\n * @param bodyFile - Absolute path to a file containing the new body\n */\nexport async function updatePrBody(\n\tprNumber: number,\n\tbodyFile: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\n\t\t\t\"gh\",\n\t\t\t[\"pr\", \"edit\", String(prNumber), \"--body-file\", bodyFile],\n\t\t\t{ cwd },\n\t\t);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"403\") || message.includes(\"insufficient\")) {\n\t\t\tthrow new DubError(\n\t\t\t\t\"GitHub token lacks required permissions. Run 'gh auth login' with the 'repo' scope.\",\n\t\t\t);\n\t\t}\n\t\tthrow new DubError(`Failed to update PR #${prNumber}: ${message}`);\n\t}\n}\n","import type { Branch } from \"./state\";\n\ninterface StackEntry {\n\tnumber: number;\n\ttitle: string;\n}\n\nconst DUBSTACK_START = \"<!-- dubstack:start -->\";\nconst DUBSTACK_END = \"<!-- dubstack:end -->\";\nconst METADATA_START = \"<!-- dubstack-metadata\";\nconst METADATA_END = \"-->\";\n\n/**\n * Builds the visible stack navigation table wrapped in dubstack markers.\n *\n * @param orderedBranches - Non-root branches in topological order\n * @param prMap - Map of branch name → PR number + title\n * @param currentBranch - The branch to mark with 👈\n */\nexport function buildStackTable(\n\torderedBranches: Branch[],\n\tprMap: Map<string, StackEntry>,\n\tcurrentBranch: string,\n): string {\n\tconst lines = orderedBranches.map((branch) => {\n\t\tconst entry = prMap.get(branch.name);\n\t\tif (!entry) return `- ${branch.name}`;\n\t\tconst marker = branch.name === currentBranch ? \" 👈\" : \"\";\n\t\treturn `- #${entry.number} ${entry.title}${marker}`;\n\t});\n\n\treturn [\n\t\tDUBSTACK_START,\n\t\t\"---\",\n\t\t\"### 🥞 DubStack\",\n\t\t...lines,\n\t\tDUBSTACK_END,\n\t].join(\"\\n\");\n}\n\n/**\n * Builds the hidden metadata HTML comment block.\n */\nexport function buildMetadataBlock(\n\tstackId: string,\n\tprNumber: number,\n\tprevPr: number | null,\n\tnextPr: number | null,\n\tbranch: string,\n): string {\n\tconst metadata = {\n\t\tstack_id: stackId,\n\t\tpr_number: prNumber,\n\t\tprev_pr: prevPr,\n\t\tnext_pr: nextPr,\n\t\tbranch,\n\t};\n\treturn `${METADATA_START}\\n${JSON.stringify(metadata, null, 2)}\\n${METADATA_END}`;\n}\n\n/**\n * Strips all DubStack-generated sections from a PR body.\n * Preserves user-written content. Returns body unchanged if no markers exist.\n */\nexport function stripDubstackSections(body: string): string {\n\tlet result = body;\n\n\tconst startIdx = result.indexOf(DUBSTACK_START);\n\tconst endIdx = result.indexOf(DUBSTACK_END);\n\tif (startIdx !== -1 && endIdx !== -1) {\n\t\tresult =\n\t\t\tresult.slice(0, startIdx) + result.slice(endIdx + DUBSTACK_END.length);\n\t}\n\n\tconst metaStart = result.indexOf(METADATA_START);\n\tif (metaStart !== -1) {\n\t\tconst metaEnd = result.indexOf(\n\t\t\tMETADATA_END,\n\t\t\tmetaStart + METADATA_START.length,\n\t\t);\n\t\tif (metaEnd !== -1) {\n\t\t\tresult =\n\t\t\t\tresult.slice(0, metaStart) +\n\t\t\t\tresult.slice(metaEnd + METADATA_END.length);\n\t\t}\n\t}\n\n\treturn result.trimEnd();\n}\n\n/**\n * Composes the final PR body by combining user content with DubStack sections.\n *\n * @param existingBody - The existing PR body (may contain stale DubStack sections)\n * @param stackTable - Output of `buildStackTable`\n * @param metadataBlock - Output of `buildMetadataBlock`\n */\nexport function composePrBody(\n\texistingBody: string,\n\tstackTable: string,\n\tmetadataBlock: string,\n): string {\n\tconst userContent = stripDubstackSections(existingBody);\n\tconst parts = [userContent, stackTable, metadataBlock].filter(Boolean);\n\treturn parts.join(\"\\n\\n\");\n}\n","import { DubError } from \"../lib/errors\";\nimport {\n\tcheckoutBranch,\n\tdeleteBranch,\n\tforceBranchTo,\n\tgetCurrentBranch,\n\tisWorkingTreeClean,\n} from \"../lib/git\";\nimport { writeState } from \"../lib/state\";\nimport { clearUndoEntry, readUndoEntry } from \"../lib/undo-log\";\n\ninterface UndoResult {\n\tundone: \"create\" | \"restack\";\n\tdetails: string;\n}\n\n/**\n * Undoes the last `dub create` or `dub restack` operation.\n *\n * Reversal strategy:\n * - **create**: Deletes the created branch, restores state, checks out the previous branch.\n * - **restack**: Resets every rebased branch to its pre-rebase tip via `git branch -f`,\n * restores state, checks out the previous branch.\n *\n * Only one level of undo is supported. After undo, the undo entry is cleared.\n *\n * @param cwd - Working directory\n * @returns What was undone and a human-readable summary\n * @throws {DubError} If nothing to undo or working tree is dirty\n */\nexport async function undo(cwd: string): Promise<UndoResult> {\n\tconst entry = await readUndoEntry(cwd);\n\n\tif (!(await isWorkingTreeClean(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Working tree has uncommitted changes. Commit or stash them before undoing.\",\n\t\t);\n\t}\n\n\tconst currentBranch = await getCurrentBranch(cwd);\n\n\tif (entry.operation === \"create\") {\n\t\t// If we're on a branch that's about to be deleted, switch away first\n\t\tconst needsCheckout = entry.createdBranches.includes(currentBranch);\n\t\tif (needsCheckout) {\n\t\t\tawait checkoutBranch(entry.previousBranch, cwd);\n\t\t}\n\n\t\tfor (const branch of entry.createdBranches) {\n\t\t\tawait deleteBranch(branch, cwd);\n\t\t}\n\n\t\tif (!needsCheckout && currentBranch !== entry.previousBranch) {\n\t\t\tawait checkoutBranch(entry.previousBranch, cwd);\n\t\t}\n\n\t\tawait writeState(entry.previousState, cwd);\n\t\tawait clearUndoEntry(cwd);\n\n\t\treturn {\n\t\t\tundone: \"create\",\n\t\t\tdetails: `Deleted branch${entry.createdBranches.length > 1 ? \"es\" : \"\"} '${entry.createdBranches.join(\"', '\")}'`,\n\t\t};\n\t}\n\n\t// restack undo: reset all branches to their pre-rebase tips\n\t// First checkout a safe branch so we don't conflict with force-moves\n\tawait checkoutBranch(entry.previousBranch, cwd);\n\n\tfor (const [name, sha] of Object.entries(entry.branchTips)) {\n\t\tif (name === entry.previousBranch) continue; // skip the branch we're on\n\t\tawait forceBranchTo(name, sha, cwd);\n\t}\n\n\t// Now force the branch we're on (if it was tracked)\n\tif (entry.branchTips[entry.previousBranch]) {\n\t\tawait forceBranchTo(\n\t\t\tentry.previousBranch,\n\t\t\tentry.branchTips[entry.previousBranch],\n\t\t\tcwd,\n\t\t);\n\t}\n\n\tawait writeState(entry.previousState, cwd);\n\tawait clearUndoEntry(cwd);\n\n\treturn {\n\t\tundone: \"restack\",\n\t\tdetails: `Reset ${Object.keys(entry.branchTips).length} branches to pre-restack state`,\n\t};\n}\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","fs","path","execa","execa","require"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/errors.ts","../src/lib/skills.ts","../src/commands/skills.ts","../src/index.ts","../src/commands/checkout.ts","../src/lib/git.ts","../src/lib/state.ts","../src/commands/create.ts","../src/lib/undo-log.ts","../src/commands/init.ts","../src/commands/log.ts","../src/commands/restack.ts","../src/commands/submit.ts","../src/lib/github.ts","../src/lib/pr-body.ts","../src/commands/undo.ts"],"sourcesContent":["/**\n * Base error class for all user-facing DubStack errors.\n *\n * The CLI entry point catches instances of this class and prints\n * a clean, colored error message. Unknown errors get a full stack trace.\n *\n * @example\n * ```ts\n * throw new DubError(\"Branch 'feat/x' already exists\")\n * ```\n */\nexport class DubError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"DubError\";\n\t}\n}\n","export const AVAILABLE_SKILLS = {\n\tdubstack: \"wiseiodev/dubstack/skills/dubstack\",\n\t\"dub-flow\": \"wiseiodev/dubstack/skills/dub-flow\",\n} as const;\n\nexport type SkillName = keyof typeof AVAILABLE_SKILLS;\n\nexport function getSkillRemote(name: SkillName): string {\n\treturn AVAILABLE_SKILLS[name];\n}\n","import chalk from \"chalk\";\nimport { execa } from \"execa\";\nimport { DubError } from \"../lib/errors\";\nimport {\n\tAVAILABLE_SKILLS,\n\tgetSkillRemote,\n\ttype SkillName,\n} from \"../lib/skills\";\n\ninterface SkillsOptions {\n\tglobal?: boolean;\n\tdryRun?: boolean;\n}\n\nfunction validateSkills(skills: string[]): SkillName[] {\n\tconst invalidSkills = skills.filter((s) => !(s in AVAILABLE_SKILLS));\n\n\tif (invalidSkills.length > 0) {\n\t\tthrow new DubError(\n\t\t\t`Unknown skill(s): ${invalidSkills.join(\", \")}. Available skills: ${Object.keys(AVAILABLE_SKILLS).join(\", \")}`,\n\t\t);\n\t}\n\n\treturn skills as SkillName[];\n}\n\nexport async function addSkills(skills: string[], options: SkillsOptions = {}) {\n\tconst targets =\n\t\tskills.length > 0\n\t\t\t? validateSkills(skills)\n\t\t\t: (Object.keys(AVAILABLE_SKILLS) as SkillName[]);\n\n\tconsole.log(chalk.blue(`Adding ${targets.length} skill(s)...`));\n\n\tfor (const skill of targets) {\n\t\tconst remote = getSkillRemote(skill);\n\t\tconst args = [\"skills\", \"add\", remote];\n\t\tif (options.global) args.push(\"--global\");\n\n\t\tconst command = `npx ${args.join(\" \")}`;\n\t\tconsole.log(chalk.dim(`Running: ${command}`));\n\n\t\tif (!options.dryRun) {\n\t\t\ttry {\n\t\t\t\tawait execa(\"npx\", args, { stdio: \"inherit\" });\n\t\t\t\tconsole.log(chalk.green(`✔ Added skill: ${skill}`));\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.red(`✖ Failed to add skill: ${skill}`));\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport async function removeSkills(\n\tskills: string[],\n\toptions: SkillsOptions = {},\n) {\n\tconst targets =\n\t\tskills.length > 0\n\t\t\t? validateSkills(skills)\n\t\t\t: (Object.keys(AVAILABLE_SKILLS) as SkillName[]);\n\n\tconsole.log(chalk.blue(`Removing ${targets.length} skill(s)...`));\n\n\tfor (const skill of targets) {\n\t\tconst args = [\"skills\", \"remove\", skill];\n\t\tif (options.global) args.push(\"--global\");\n\n\t\tconst command = `npx ${args.join(\" \")}`;\n\t\tconsole.log(chalk.dim(`Running: ${command}`));\n\n\t\tif (!options.dryRun) {\n\t\t\ttry {\n\t\t\t\tawait execa(\"npx\", args, { stdio: \"inherit\" });\n\t\t\t\tconsole.log(chalk.green(`✔ Removed skill: ${skill}`));\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.red(`✖ Failed to remove skill: ${skill}`));\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n}\n","#!/usr/bin/env node\n\n/**\n * DubStack CLI — manage stacked diffs with ease.\n *\n * A local-first tool for managing chains of dependent git branches\n * (stacked diffs) without manually dealing with complex rebase chains.\n *\n * @example\n * ```bash\n * dub init # Initialize in current repo\n * dub create feat/my-branch # Create stacked branch\n * dub log # View stack tree\n * dub restack # Rebase stack onto updated parent\n * dub undo # Undo last dub operation\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createRequire } from \"node:module\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { checkout, interactiveCheckout } from \"./commands/checkout\";\nimport { create } from \"./commands/create\";\nimport { init } from \"./commands/init\";\nimport { log } from \"./commands/log\";\nimport { restack, restackContinue } from \"./commands/restack\";\nimport { submit } from \"./commands/submit\";\nimport { undo } from \"./commands/undo\";\nimport { DubError } from \"./lib/errors\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n\t.name(\"dub\")\n\t.description(\"Manage stacked diffs (dependent git branches) with ease\")\n\t.version(version);\n\nprogram\n\t.command(\"init\")\n\t.description(\"Initialize DubStack in the current git repository\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub init Initialize DubStack, creating .git/dubstack/ and updating .gitignore`,\n\t)\n\t.action(async () => {\n\t\tconst result = await init(process.cwd());\n\t\tif (result.status === \"created\") {\n\t\t\tconsole.log(chalk.green(\"✔ DubStack initialized\"));\n\t\t} else {\n\t\t\tconsole.log(chalk.yellow(\"⚠ DubStack already initialized\"));\n\t\t}\n\t});\n\nprogram\n\t.command(\"create\")\n\t.argument(\"<branch-name>\", \"Name of the new branch to create\")\n\t.description(\"Create a new branch stacked on top of the current branch\")\n\t.option(\"-m, --message <message>\", \"Commit staged changes with this message\")\n\t.option(\"-a, --all\", \"Stage all changes before committing (requires -m)\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub create feat/api Create branch only\n $ dub create feat/api -m \"feat: add API\" Create branch + commit staged\n $ dub create feat/api -am \"feat: add API\" Stage all + create + commit`,\n\t)\n\t.action(\n\t\tasync (\n\t\t\tbranchName: string,\n\t\t\toptions: { message?: string; all?: boolean },\n\t\t) => {\n\t\t\tconst result = await create(branchName, process.cwd(), {\n\t\t\t\tmessage: options.message,\n\t\t\t\tall: options.all,\n\t\t\t});\n\t\t\tif (result.committed) {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green(\n\t\t\t\t\t\t`✔ Created '${result.branch}' on '${result.parent}' • ${result.committed}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green(\n\t\t\t\t\t\t`✔ Created branch '${result.branch}' on top of '${result.parent}'`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command(\"log\")\n\t.description(\"Display an ASCII tree of the current stack\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub log Show the branch tree with current branch highlighted`,\n\t)\n\t.action(async () => {\n\t\tconst output = await log(process.cwd());\n\t\t// Apply chalk styling to the output\n\t\tconst styled = output\n\t\t\t.replace(/\\*(.+?) \\(Current\\)\\*/g, chalk.bold.cyan(\"$1 (Current)\"))\n\t\t\t.replace(/⚠ \\(missing\\)/g, chalk.yellow(\"⚠ (missing)\"));\n\t\tconsole.log(styled);\n\t});\n\nprogram\n\t.command(\"restack\")\n\t.description(\"Rebase all branches in the stack onto their updated parents\")\n\t.option(\"--continue\", \"Continue restacking after resolving conflicts\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub restack Rebase the current stack\n $ dub restack --continue Continue after resolving conflicts`,\n\t)\n\t.action(async (options: { continue?: boolean }) => {\n\t\tconst result = options.continue\n\t\t\t? await restackContinue(process.cwd())\n\t\t\t: await restack(process.cwd());\n\n\t\tif (result.status === \"up-to-date\") {\n\t\t\tconsole.log(chalk.green(\"✔ Stack is already up to date\"));\n\t\t} else if (result.status === \"conflict\") {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(`⚠ Conflict while restacking '${result.conflictBranch}'`),\n\t\t\t);\n\t\t\tconsole.log(\n\t\t\t\tchalk.dim(\n\t\t\t\t\t\" Resolve conflicts, stage changes, then run: dub restack --continue\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.log(\n\t\t\t\tchalk.green(`✔ Restacked ${result.rebased.length} branch(es)`),\n\t\t\t);\n\t\t\tfor (const branch of result.rebased) {\n\t\t\t\tconsole.log(chalk.dim(` ↳ ${branch}`));\n\t\t\t}\n\t\t}\n\t});\n\nprogram\n\t.command(\"undo\")\n\t.description(\"Undo the last dub create or dub restack operation\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub undo Roll back the last dub operation`,\n\t)\n\t.action(async () => {\n\t\tconst result = await undo(process.cwd());\n\t\tconsole.log(chalk.green(`✔ Undid '${result.undone}': ${result.details}`));\n\t});\n\nprogram\n\t.command(\"submit\")\n\t.description(\n\t\t\"Push branches and create/update GitHub PRs for the current stack\",\n\t)\n\t.option(\"--dry-run\", \"Print what would happen without executing\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub submit Push and create/update PRs\n $ dub submit --dry-run Preview what would happen`,\n\t)\n\t.action(runSubmit);\n\nprogram\n\t.command(\"ss\")\n\t.description(\"Submit the current stack (alias for submit)\")\n\t.option(\"--dry-run\", \"Print what would happen without executing\")\n\t.action(runSubmit);\n\nprogram\n\t.command(\"co\")\n\t.argument(\"[branch]\", \"Branch to checkout (interactive if omitted)\")\n\t.description(\"Checkout a branch (interactive picker if no name given)\")\n\t.action(async (branch?: string) => {\n\t\tif (branch) {\n\t\t\tconst result = await checkout(branch, process.cwd());\n\t\t\tconsole.log(chalk.green(`✔ Switched to '${result.branch}'`));\n\t\t} else {\n\t\t\tconst result = await interactiveCheckout(process.cwd());\n\t\t\tif (result) {\n\t\t\t\tconsole.log(chalk.green(`✔ Switched to '${result.branch}'`));\n\t\t\t}\n\t\t}\n\t});\n\nprogram\n\t.command(\"skills\")\n\t.description(\"Manage DubStack agent skills\")\n\t.addCommand(\n\t\tnew Command(\"add\")\n\t\t\t.description(\"Install agent skills (e.g. dubstack, dub-flow)\")\n\t\t\t.argument(\"[skills...]\", \"Names of skills to install (default: all)\")\n\t\t\t.option(\"-g, --global\", \"Install skills globally\")\n\t\t\t.option(\"--dry-run\", \"Preview actions without installing\")\n\t\t\t.action(async (skills, options) => {\n\t\t\t\tconst { addSkills } = await import(\"./commands/skills\");\n\t\t\t\tawait addSkills(skills, options);\n\t\t\t}),\n\t)\n\t.addCommand(\n\t\tnew Command(\"remove\")\n\t\t\t.description(\"Remove agent skills\")\n\t\t\t.argument(\"[skills...]\", \"Names of skills to remove (default: all)\")\n\t\t\t.option(\"-g, --global\", \"Remove skills globally\")\n\t\t\t.option(\"--dry-run\", \"Preview actions without removing\")\n\t\t\t.action(async (skills, options) => {\n\t\t\t\tconst { removeSkills } = await import(\"./commands/skills\");\n\t\t\t\tawait removeSkills(skills, options);\n\t\t\t}),\n\t);\n\nasync function runSubmit(options: { dryRun?: boolean }) {\n\tconst result = await submit(process.cwd(), options.dryRun ?? false);\n\n\tif (result.pushed.length > 0) {\n\t\tconsole.log(\n\t\t\tchalk.green(\n\t\t\t\t`✔ Pushed ${result.pushed.length} branch(es), created ${result.created.length} PR(s), updated ${result.updated.length} PR(s)`,\n\t\t\t),\n\t\t);\n\t\tfor (const branch of [...result.created, ...result.updated]) {\n\t\t\tconsole.log(chalk.dim(` ↳ ${branch}`));\n\t\t}\n\t}\n}\n\nasync function main() {\n\ttry {\n\t\tawait program.parseAsync(process.argv);\n\t} catch (error) {\n\t\tif (error instanceof DubError) {\n\t\t\tconsole.error(chalk.red(`✖ ${error.message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nmain();\n","import search from \"@inquirer/search\";\nimport { DubError } from \"../lib/errors\";\nimport { checkoutBranch, getCurrentBranch, listBranches } from \"../lib/git\";\nimport { type DubState, readState } from \"../lib/state\";\n\n/**\n * Returns a sorted, deduplicated list of branch names tracked by DubStack.\n * Root branches that appear in multiple stacks are included only once.\n */\nexport function getTrackedBranches(state: DubState): string[] {\n\tconst names = new Set<string>();\n\tfor (const stack of state.stacks) {\n\t\tfor (const branch of stack.branches) {\n\t\t\tnames.add(branch.name);\n\t\t}\n\t}\n\treturn [...names].sort();\n}\n\n/**\n * Filters tracked branches against the list of actual local git branches.\n * Removes any branches that are tracked in state but have been deleted locally.\n */\nexport function getValidBranches(tracked: string[], local: string[]): string[] {\n\tconst localSet = new Set(local);\n\treturn tracked.filter((b) => localSet.has(b));\n}\n\n/**\n * Checks out the named branch.\n *\n * @param name - Branch to switch to\n * @param cwd - Working directory\n * @returns The checked-out branch name\n * @throws {DubError} If the branch does not exist\n */\nexport async function checkout(\n\tname: string,\n\tcwd: string,\n): Promise<{ branch: string }> {\n\tawait checkoutBranch(name, cwd);\n\treturn { branch: name };\n}\n\n/**\n * Launches an interactive search prompt listing DubStack-tracked branches.\n *\n * The current branch is shown but disabled. The user can type to filter,\n * use arrow keys to navigate, and press Enter to checkout.\n *\n * @param cwd - Working directory\n * @returns The checked-out branch, or `null` if the user cancelled (Ctrl+C)\n * @throws {DubError} If not initialized or no tracked branches exist\n */\nexport async function interactiveCheckout(\n\tcwd: string,\n): Promise<{ branch: string } | null> {\n\tconst state = await readState(cwd);\n\tconst trackedBranches = getTrackedBranches(state);\n\tconst localBranches = await listBranches(cwd);\n\n\tconst validBranches = getValidBranches(trackedBranches, localBranches);\n\n\tif (validBranches.length === 0) {\n\t\tthrow new DubError(\n\t\t\t\"No valid tracked branches found. Run 'dub create' first.\",\n\t\t);\n\t}\n\n\tlet currentBranch: string | null = null;\n\ttry {\n\t\tcurrentBranch = await getCurrentBranch(cwd);\n\t} catch {\n\t\t// Detached HEAD — no branch marked as current\n\t}\n\n\t// Setup AbortController for Esc key support\n\tconst controller = new AbortController();\n\n\t// Listen for keypress events to handle Esc\n\tconst onKeypress = (_str: string, key: { name: string; ctrl: boolean }) => {\n\t\tif (key && key.name === \"escape\") {\n\t\t\tcontroller.abort();\n\t\t}\n\t};\n\tprocess.stdin.on(\"keypress\", onKeypress);\n\n\ttry {\n\t\tconst selected = await search(\n\t\t\t{\n\t\t\t\tmessage: \"Checkout a branch (autocomplete or arrow keys)\",\n\t\t\t\tsource(term: string | undefined) {\n\t\t\t\t\tconst filtered = term\n\t\t\t\t\t\t? validBranches.filter((b) =>\n\t\t\t\t\t\t\t\tb.toLowerCase().includes(term.toLowerCase()),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: validBranches;\n\n\t\t\t\t\treturn filtered.map((name) => ({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tvalue: name,\n\t\t\t\t\t\tdisabled: name === currentBranch ? \"(current)\" : false,\n\t\t\t\t\t}));\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ signal: controller.signal },\n\t\t);\n\n\t\treturn checkout(selected, cwd);\n\t} catch (error: unknown) {\n\t\tif (error instanceof Error) {\n\t\t\tif (\n\t\t\t\terror.name === \"ExitPromptError\" ||\n\t\t\t\terror.name === \"AbortError\" ||\n\t\t\t\terror.name === \"AbortPromptError\"\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\tprocess.stdin.off(\"keypress\", onKeypress);\n\t}\n}\n","import { execa } from \"execa\";\nimport { DubError } from \"./errors\";\n\n/**\n * Checks whether the given directory is inside a git repository.\n * @returns `true` if inside a git worktree, `false` otherwise. Never throws.\n */\nexport async function isGitRepo(cwd: string): Promise<boolean> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"rev-parse\", \"--is-inside-work-tree\"],\n\t\t\t{ cwd },\n\t\t);\n\t\treturn stdout.trim() === \"true\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Returns the absolute path to the repository root.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getRepoRoot(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n\t\t\tcwd,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"Not a git repository. Run this command inside a git repo.\",\n\t\t);\n\t}\n}\n\n/**\n * Returns the name of the currently checked-out branch.\n * @throws {DubError} If HEAD is detached or the repo has no commits.\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"rev-parse\", \"--abbrev-ref\", \"HEAD\"],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst branch = stdout.trim();\n\t\tif (branch === \"HEAD\") {\n\t\t\tthrow new DubError(\n\t\t\t\t\"HEAD is detached. Checkout a branch before running this command.\",\n\t\t\t);\n\t\t}\n\t\treturn branch;\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(\n\t\t\t\"Repository has no commits. Make at least one commit first.\",\n\t\t);\n\t}\n}\n\n/**\n * Checks whether a branch with the given name exists locally.\n * @returns `true` if the branch exists, `false` otherwise. Never throws.\n */\nexport async function branchExists(\n\tname: string,\n\tcwd: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"git\", [\"rev-parse\", \"--verify\", `refs/heads/${name}`], {\n\t\t\tcwd,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Creates a new branch and switches to it.\n * @throws {DubError} If a branch with that name already exists.\n */\nexport async function createBranch(name: string, cwd: string): Promise<void> {\n\tif (await branchExists(name, cwd)) {\n\t\tthrow new DubError(`Branch '${name}' already exists.`);\n\t}\n\tawait execa(\"git\", [\"checkout\", \"-b\", name], { cwd });\n}\n\n/**\n * Switches to an existing branch.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function checkoutBranch(name: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"checkout\", name], { cwd });\n\t} catch {\n\t\tthrow new DubError(`Branch '${name}' not found.`);\n\t}\n}\n\n/**\n * Deletes a local branch forcefully. Used by undo to remove created branches.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function deleteBranch(name: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"branch\", \"-D\", name], { cwd });\n\t} catch {\n\t\tthrow new DubError(`Failed to delete branch '${name}'. It may not exist.`);\n\t}\n}\n\n/**\n * Force-moves a branch pointer to a specific commit SHA.\n * Used by undo to reset branches to their pre-operation tips.\n */\nexport async function forceBranchTo(\n\tname: string,\n\tsha: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tconst current = await getCurrentBranch(cwd).catch(() => null);\n\t\tif (current === name) {\n\t\t\tawait execa(\"git\", [\"reset\", \"--hard\", sha], { cwd });\n\t\t} else {\n\t\t\tawait execa(\"git\", [\"branch\", \"-f\", name, sha], { cwd });\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(`Failed to reset branch '${name}' to ${sha}.`);\n\t}\n}\n\n/**\n * Checks whether the working tree is clean (no uncommitted changes).\n * @returns `true` if clean (no output from `git status --porcelain`).\n */\nexport async function isWorkingTreeClean(cwd: string): Promise<boolean> {\n\tconst { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"], { cwd });\n\treturn stdout.trim() === \"\";\n}\n\n/**\n * Performs `git rebase --onto` to move a branch from one base to another.\n *\n * @param newBase - The commit/branch to rebase onto\n * @param oldBase - The old base commit to replay from\n * @param branch - The branch being rebased\n * @throws {DubError} If a merge conflict occurs during rebase\n */\nexport async function rebaseOnto(\n\tnewBase: string,\n\toldBase: string,\n\tbranch: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"rebase\", \"--onto\", newBase, oldBase, branch], { cwd });\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Conflict while restacking '${branch}'.\\n` +\n\t\t\t\t\" Resolve conflicts, stage changes, then run: dub restack --continue\",\n\t\t);\n\t}\n}\n\n/**\n * Continues an in-progress rebase after conflicts have been resolved.\n * @throws {DubError} If the rebase continue fails.\n */\nexport async function rebaseContinue(cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"rebase\", \"--continue\"], {\n\t\t\tcwd,\n\t\t\tenv: { GIT_EDITOR: \"true\" },\n\t\t});\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"Failed to continue rebase. Ensure all conflicts are resolved and staged.\",\n\t\t);\n\t}\n}\n\n/**\n * Returns the merge-base (common ancestor) commit SHA of two branches.\n */\nexport async function getMergeBase(\n\ta: string,\n\tb: string,\n\tcwd: string,\n): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"merge-base\", a, b], { cwd });\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Could not find common ancestor between '${a}' and '${b}'.`,\n\t\t);\n\t}\n}\n\n/**\n * Returns the commit SHA at the tip of a branch.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function getBranchTip(name: string, cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"rev-parse\", name], { cwd });\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(`Branch '${name}' not found.`);\n\t}\n}\n\n/**\n * Returns the subject line of the most recent commit on a branch.\n * @throws {DubError} If the branch has no commits.\n */\nexport async function getLastCommitMessage(\n\tbranch: string,\n\tcwd: string,\n): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"log\", \"-1\", \"--format=%s\", branch],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst message = stdout.trim();\n\t\tif (!message) {\n\t\t\tthrow new DubError(`Branch '${branch}' has no commits.`);\n\t\t}\n\t\treturn message;\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(`Failed to read commit message for '${branch}'.`);\n\t}\n}\n\n/**\n * Pushes a branch to origin with `--force-with-lease`.\n * @throws {DubError} If the push fails.\n */\nexport async function pushBranch(branch: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"push\", \"--force-with-lease\", \"origin\", branch], {\n\t\t\tcwd,\n\t\t});\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Failed to push '${branch}'. The remote ref may have been updated by someone else.`,\n\t\t);\n\t}\n}\n\n/**\n * Stages all changes (tracked, untracked, and deletions).\n * @throws {DubError} If git add fails.\n */\nexport async function stageAll(cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"add\", \"-A\"], { cwd });\n\t} catch {\n\t\tthrow new DubError(\"Failed to stage changes.\");\n\t}\n}\n\n/**\n * Checks whether there are staged changes ready to commit.\n *\n * `git diff --cached --quiet` exits with code 1 when changes exist\n * and code 0 when there are none.\n */\nexport async function hasStagedChanges(cwd: string): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"git\", [\"diff\", \"--cached\", \"--quiet\"], { cwd });\n\t\treturn false;\n\t} catch (error: unknown) {\n\t\tconst exitCode = (error as { exitCode?: number }).exitCode;\n\t\tif (exitCode === 1) return true;\n\t\tthrow new DubError(\"Failed to check staged changes.\");\n\t}\n}\n\n/**\n * Commits currently staged changes with the given message.\n * @throws {DubError} If the commit fails (e.g., nothing staged, hook rejection).\n */\nexport async function commitStaged(\n\tmessage: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"commit\", \"-m\", message], { cwd });\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Commit failed. Ensure there are staged changes and git hooks pass.`,\n\t\t);\n\t}\n}\n\n/**\n * Returns a list of all local branch names.\n */\nexport async function listBranches(cwd: string): Promise<string[]> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"branch\", \"--format=%(refname:short)\"],\n\t\t\t{ cwd },\n\t\t);\n\t\treturn stdout.trim().split(\"\\n\").filter(Boolean);\n\t} catch {\n\t\tthrow new DubError(\"Failed to list branches.\");\n\t}\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"./errors\";\nimport { getRepoRoot } from \"./git\";\n\n/** A branch within a stack. */\nexport interface Branch {\n\t/** Branch name, e.g. \"feat/api-endpoint\" */\n\tname: string;\n\t/** Set to \"root\" for the base branch (e.g. main). Omitted for children. */\n\ttype?: \"root\";\n\t/** Name of the parent branch. `null` only for root branches. */\n\tparent: string | null;\n\t/** GitHub PR number. Populated after `dub submit`. */\n\tpr_number: number | null;\n\t/** GitHub PR URL. Populated after `dub submit`. */\n\tpr_link: string | null;\n}\n\n/** A stack of dependent branches. */\nexport interface Stack {\n\t/** Unique identifier for this stack. */\n\tid: string;\n\t/** Ordered list of branches in the stack. */\n\tbranches: Branch[];\n}\n\n/** Root state persisted to `.git/dubstack/state.json`. */\nexport interface DubState {\n\t/** All tracked stacks in this repository. */\n\tstacks: Stack[];\n}\n\n/**\n * Returns the absolute path to the dubstack state file.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getStatePath(cwd: string): Promise<string> {\n\tconst root = await getRepoRoot(cwd);\n\treturn path.join(root, \".git\", \"dubstack\", \"state.json\");\n}\n\n/**\n * Returns the absolute path to the dubstack directory inside `.git`.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getDubDir(cwd: string): Promise<string> {\n\tconst root = await getRepoRoot(cwd);\n\treturn path.join(root, \".git\", \"dubstack\");\n}\n\n/**\n * Reads and parses the dubstack state file.\n * @throws {DubError} If the state file is missing or contains invalid JSON.\n */\nexport async function readState(cwd: string): Promise<DubState> {\n\tconst statePath = await getStatePath(cwd);\n\tif (!fs.existsSync(statePath)) {\n\t\tthrow new DubError(\"DubStack is not initialized. Run 'dub init' first.\");\n\t}\n\ttry {\n\t\tconst raw = fs.readFileSync(statePath, \"utf-8\");\n\t\treturn JSON.parse(raw) as DubState;\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"State file is corrupted. Delete .git/dubstack and run 'dub init' to re-initialize.\",\n\t\t);\n\t}\n}\n\n/**\n * Writes the dubstack state to disk.\n * Creates the parent directory if it doesn't exist.\n */\nexport async function writeState(state: DubState, cwd: string): Promise<void> {\n\tconst statePath = await getStatePath(cwd);\n\tconst dir = path.dirname(statePath);\n\tif (!fs.existsSync(dir)) {\n\t\tfs.mkdirSync(dir, { recursive: true });\n\t}\n\tfs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\n/**\n * Initializes the dubstack state directory and file.\n * Idempotent — returns `\"already_exists\"` if already initialized.\n *\n * @returns `\"created\"` if freshly initialized, `\"already_exists\"` if state file already present.\n */\nexport async function initState(\n\tcwd: string,\n): Promise<\"created\" | \"already_exists\"> {\n\tconst statePath = await getStatePath(cwd);\n\tconst dir = path.dirname(statePath);\n\n\tif (fs.existsSync(statePath)) {\n\t\treturn \"already_exists\";\n\t}\n\n\tfs.mkdirSync(dir, { recursive: true });\n\tconst emptyState: DubState = { stacks: [] };\n\tfs.writeFileSync(statePath, `${JSON.stringify(emptyState, null, 2)}\\n`);\n\treturn \"created\";\n}\n\n/**\n * Returns state, auto-initializing if not yet set up.\n * Only catches the \"not initialized\" error — corrupt state still throws.\n */\nexport async function ensureState(cwd: string): Promise<DubState> {\n\ttry {\n\t\treturn await readState(cwd);\n\t} catch (error) {\n\t\tif (\n\t\t\terror instanceof DubError &&\n\t\t\terror.message.includes(\"not initialized\")\n\t\t) {\n\t\t\tawait initState(cwd);\n\t\t\treturn await readState(cwd);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Finds the stack containing a given branch.\n * @returns The matching stack, or `undefined` if the branch isn't tracked.\n */\nexport function findStackForBranch(\n\tstate: DubState,\n\tname: string,\n): Stack | undefined {\n\treturn state.stacks.find((stack) =>\n\t\tstack.branches.some((b) => b.name === name),\n\t);\n}\n\n/**\n * Adds a child branch to the state, linking it to its parent.\n *\n * Decision tree:\n * 1. If `child` already exists in any stack → throws `DubError` (no duplicates)\n * 2. If `parent` is found in an existing stack → appends child to that stack\n * 3. If `parent` is not in any stack → creates a new stack with parent as root\n *\n * @param state - The state to mutate (modified in place)\n * @param child - Name of the new branch\n * @param parent - Name of the parent branch\n * @throws {DubError} If child branch already exists in state\n */\nexport function addBranchToStack(\n\tstate: DubState,\n\tchild: string,\n\tparent: string,\n): void {\n\tif (findStackForBranch(state, child)) {\n\t\tthrow new DubError(`Branch '${child}' is already tracked in a stack.`);\n\t}\n\n\tconst childBranch: Branch = {\n\t\tname: child,\n\t\tparent,\n\t\tpr_number: null,\n\t\tpr_link: null,\n\t};\n\tconst existingStack = findStackForBranch(state, parent);\n\n\tif (existingStack) {\n\t\texistingStack.branches.push(childBranch);\n\t} else {\n\t\tconst rootBranch: Branch = {\n\t\t\tname: parent,\n\t\t\ttype: \"root\",\n\t\t\tparent: null,\n\t\t\tpr_number: null,\n\t\t\tpr_link: null,\n\t\t};\n\t\tstate.stacks.push({\n\t\t\tid: crypto.randomUUID(),\n\t\t\tbranches: [rootBranch, childBranch],\n\t\t});\n\t}\n}\n\n/**\n * Returns branches in topological (BFS) order starting from the root.\n * Useful for operations that need to process parent branches before children.\n */\nexport function topologicalOrder(stack: Stack): Branch[] {\n\tconst result: Branch[] = [];\n\tconst root = stack.branches.find((b) => b.type === \"root\");\n\tif (!root) return result;\n\n\tconst childMap = new Map<string, Branch[]>();\n\tfor (const branch of stack.branches) {\n\t\tif (branch.parent) {\n\t\t\tconst children = childMap.get(branch.parent) ?? [];\n\t\t\tchildren.push(branch);\n\t\t\tchildMap.set(branch.parent, children);\n\t\t}\n\t}\n\n\tconst queue = [root];\n\twhile (queue.length > 0) {\n\t\tconst current = queue.shift();\n\t\tif (!current) break;\n\t\tresult.push(current);\n\t\tconst children = childMap.get(current.name) ?? [];\n\t\tqueue.push(...children);\n\t}\n\n\treturn result;\n}\n","import { DubError } from \"../lib/errors\";\nimport {\n\tbranchExists,\n\tcommitStaged,\n\tcreateBranch,\n\tgetCurrentBranch,\n\thasStagedChanges,\n\tstageAll,\n} from \"../lib/git\";\nimport { addBranchToStack, ensureState, writeState } from \"../lib/state\";\nimport { saveUndoEntry } from \"../lib/undo-log\";\n\ninterface CreateOptions {\n\tmessage?: string;\n\tall?: boolean;\n}\n\ninterface CreateResult {\n\tbranch: string;\n\tparent: string;\n\tcommitted?: string;\n}\n\n/**\n * Creates a new branch stacked on top of the current branch.\n *\n * When `-m` is provided, also commits staged changes on the new branch.\n * When `-a` is provided, stages all changes first (requires `-m`).\n *\n * @param name - Name of the new branch to create\n * @param cwd - Working directory (auto-initializes if needed)\n * @param options - Optional message and all flags\n * @returns The created branch name, its parent, and committed message if applicable\n * @throws {DubError} If branch exists, HEAD is detached, -a without -m, or nothing to commit\n */\nexport async function create(\n\tname: string,\n\tcwd: string,\n\toptions?: CreateOptions,\n): Promise<CreateResult> {\n\tif (options?.all && !options.message) {\n\t\tthrow new DubError(\"'-a' requires '-m'. Pass a commit message.\");\n\t}\n\n\tconst state = await ensureState(cwd);\n\tconst parent = await getCurrentBranch(cwd);\n\n\tif (await branchExists(name, cwd)) {\n\t\tthrow new DubError(`Branch '${name}' already exists.`);\n\t}\n\n\tif (options?.message) {\n\t\tif (options.all) {\n\t\t\tawait stageAll(cwd);\n\t\t}\n\n\t\tif (!(await hasStagedChanges(cwd))) {\n\t\t\tconst hint = options.all\n\t\t\t\t? \"No changes to commit.\"\n\t\t\t\t: \"No staged changes. Stage files with 'git add' or use '-a' to stage all.\";\n\t\t\tthrow new DubError(hint);\n\t\t}\n\t}\n\n\tawait saveUndoEntry(\n\t\t{\n\t\t\toperation: \"create\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpreviousBranch: parent,\n\t\t\tpreviousState: structuredClone(state),\n\t\t\tbranchTips: {},\n\t\t\tcreatedBranches: [name],\n\t\t},\n\t\tcwd,\n\t);\n\n\tawait createBranch(name, cwd);\n\taddBranchToStack(state, name, parent);\n\tawait writeState(state, cwd);\n\n\tif (options?.message) {\n\t\ttry {\n\t\t\tawait commitStaged(options.message, cwd);\n\t\t} catch (error) {\n\t\t\tconst reason = error instanceof DubError ? error.message : String(error);\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${name}' was created but commit failed: ${reason}. Run 'dub undo' to clean up.`,\n\t\t\t);\n\t\t}\n\t\treturn { branch: name, parent, committed: options.message };\n\t}\n\n\treturn { branch: name, parent };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"./errors\";\nimport type { DubState } from \"./state\";\nimport { getDubDir } from \"./state\";\n\n/**\n * Snapshot of system state before a mutation, used by `dub undo`.\n * Only one undo level is supported — each new mutation overwrites the previous snapshot.\n */\nexport interface UndoEntry {\n\t/** Which command created this snapshot. */\n\toperation: \"create\" | \"restack\";\n\t/** ISO timestamp of when the snapshot was taken. */\n\ttimestamp: string;\n\t/** The branch user was on before the operation. */\n\tpreviousBranch: string;\n\t/** Full copy of state.json before mutation. */\n\tpreviousState: DubState;\n\t/** Map of branch name → commit SHA before mutation. */\n\tbranchTips: Record<string, string>;\n\t/** Branches created by this operation (to be deleted on undo). */\n\tcreatedBranches: string[];\n}\n\nasync function getUndoPath(cwd: string): Promise<string> {\n\tconst dubDir = await getDubDir(cwd);\n\treturn path.join(dubDir, \"undo.json\");\n}\n\n/**\n * Saves an undo entry to disk. Overwrites any previous entry (1 level only).\n */\nexport async function saveUndoEntry(\n\tentry: UndoEntry,\n\tcwd: string,\n): Promise<void> {\n\tconst undoPath = await getUndoPath(cwd);\n\tfs.writeFileSync(undoPath, `${JSON.stringify(entry, null, 2)}\\n`);\n}\n\n/**\n * Reads the most recent undo entry.\n * @throws {DubError} If no undo entry exists.\n */\nexport async function readUndoEntry(cwd: string): Promise<UndoEntry> {\n\tconst undoPath = await getUndoPath(cwd);\n\tif (!fs.existsSync(undoPath)) {\n\t\tthrow new DubError(\"Nothing to undo.\");\n\t}\n\tconst raw = fs.readFileSync(undoPath, \"utf-8\");\n\treturn JSON.parse(raw) as UndoEntry;\n}\n\n/**\n * Deletes the undo entry file. Called after a successful undo.\n */\nexport async function clearUndoEntry(cwd: string): Promise<void> {\n\tconst undoPath = await getUndoPath(cwd);\n\tif (fs.existsSync(undoPath)) {\n\t\tfs.unlinkSync(undoPath);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport { getRepoRoot, isGitRepo } from \"../lib/git\";\nimport { initState } from \"../lib/state\";\n\ninterface InitResult {\n\tstatus: \"created\" | \"already_exists\";\n\tgitignoreUpdated: boolean;\n}\n\n/**\n * Initializes DubStack in the current git repository.\n *\n * Creates `.git/dubstack/state.json` with an empty state and ensures\n * `.git/dubstack` is listed in `.gitignore`. Idempotent — safe to run\n * multiple times.\n *\n * @param cwd - Working directory (must be inside a git repo)\n * @returns Status indicating whether state was created or already existed\n * @throws {DubError} If not inside a git repository\n */\nexport async function init(cwd: string): Promise<InitResult> {\n\tif (!(await isGitRepo(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Not a git repository. Run this command inside a git repo.\",\n\t\t);\n\t}\n\n\tconst status = await initState(cwd);\n\tconst repoRoot = await getRepoRoot(cwd);\n\tconst gitignorePath = path.join(repoRoot, \".gitignore\");\n\tconst entry = \".git/dubstack\";\n\tlet gitignoreUpdated = false;\n\n\tif (fs.existsSync(gitignorePath)) {\n\t\tconst content = fs.readFileSync(gitignorePath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\");\n\t\tif (!lines.some((line) => line.trim() === entry)) {\n\t\t\tconst separator = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n\t\t\tfs.writeFileSync(gitignorePath, `${content}${separator}${entry}\\n`);\n\t\t\tgitignoreUpdated = true;\n\t\t}\n\t} else {\n\t\tfs.writeFileSync(gitignorePath, `${entry}\\n`);\n\t\tgitignoreUpdated = true;\n\t}\n\n\treturn { status, gitignoreUpdated };\n}\n","import { branchExists, getCurrentBranch } from \"../lib/git\";\nimport type { Branch, Stack } from \"../lib/state\";\nimport { readState } from \"../lib/state\";\n\n/**\n * Renders an ASCII tree view of all tracked stacks.\n *\n * Highlights the current branch, marks branches missing from git,\n * and handles multiple stacks separated by blank lines.\n *\n * @param cwd - Working directory (must be inside an initialized dubstack repo)\n * @returns Formatted ASCII tree string (no ANSI colors — caller adds chalk)\n * @throws {DubError} If not initialized\n */\nexport async function log(cwd: string): Promise<string> {\n\tconst state = await readState(cwd);\n\n\tif (state.stacks.length === 0) {\n\t\treturn \"No stacks. Run 'dub create' to start.\";\n\t}\n\n\tlet currentBranch: string | null = null;\n\ttry {\n\t\tcurrentBranch = await getCurrentBranch(cwd);\n\t} catch {\n\t\t// Detached HEAD or empty repo — no branch highlighted\n\t}\n\n\tconst sections: string[] = [];\n\n\tfor (const stack of state.stacks) {\n\t\tconst tree = await renderStack(stack, currentBranch, cwd);\n\t\tsections.push(tree);\n\t}\n\n\treturn sections.join(\"\\n\\n\");\n}\n\nasync function renderStack(\n\tstack: Stack,\n\tcurrentBranch: string | null,\n\tcwd: string,\n): Promise<string> {\n\tconst root = stack.branches.find((b) => b.type === \"root\");\n\tif (!root) return \"\";\n\n\tconst childMap = new Map<string, Branch[]>();\n\tfor (const branch of stack.branches) {\n\t\tif (branch.parent) {\n\t\t\tconst children = childMap.get(branch.parent) ?? [];\n\t\t\tchildren.push(branch);\n\t\t\tchildMap.set(branch.parent, children);\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\tawait renderNode(root, currentBranch, childMap, \"\", true, true, lines, cwd);\n\treturn lines.join(\"\\n\");\n}\n\nasync function renderNode(\n\tbranch: Branch,\n\tcurrentBranch: string | null,\n\tchildMap: Map<string, Branch[]>,\n\tprefix: string,\n\tisRoot: boolean,\n\tisLast: boolean,\n\tlines: string[],\n\tcwd: string,\n): Promise<void> {\n\tlet label: string;\n\tconst exists = await branchExists(branch.name, cwd);\n\n\tif (isRoot) {\n\t\tlabel = `(${branch.name})`;\n\t} else if (branch.name === currentBranch) {\n\t\tlabel = `*${branch.name} (Current)*`;\n\t} else if (!exists) {\n\t\tlabel = `${branch.name} ⚠ (missing)`;\n\t} else {\n\t\tlabel = branch.name;\n\t}\n\n\tif (isRoot) {\n\t\tlines.push(label);\n\t} else {\n\t\tconst connector = isLast ? \"└─ \" : \"├─ \";\n\t\tlines.push(`${prefix}${connector}${label}`);\n\t}\n\n\tconst children = childMap.get(branch.name) ?? [];\n\tconst childPrefix = isRoot ? \" \" : `${prefix}${isLast ? \" \" : \"│ \"}`;\n\n\tfor (let i = 0; i < children.length; i++) {\n\t\tconst isChildLast = i === children.length - 1;\n\t\tawait renderNode(\n\t\t\tchildren[i],\n\t\t\tcurrentBranch,\n\t\t\tchildMap,\n\t\t\tchildPrefix,\n\t\t\tfalse,\n\t\t\tisChildLast,\n\t\t\tlines,\n\t\t\tcwd,\n\t\t);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport {\n\tbranchExists,\n\tcheckoutBranch,\n\tgetBranchTip,\n\tgetCurrentBranch,\n\tgetMergeBase,\n\trebaseContinue as gitRebaseContinue,\n\tisWorkingTreeClean,\n\trebaseOnto,\n} from \"../lib/git\";\nimport {\n\tgetDubDir,\n\treadState,\n\ttype Stack,\n\ttopologicalOrder,\n} from \"../lib/state\";\nimport { saveUndoEntry } from \"../lib/undo-log\";\n\ninterface RestackStep {\n\tbranch: string;\n\tparent: string;\n\tparentOldTip: string;\n\tstatus: \"pending\" | \"done\" | \"skipped\" | \"conflicted\";\n}\n\ninterface RestackProgress {\n\toriginalBranch: string;\n\tsteps: RestackStep[];\n}\n\ninterface RestackResult {\n\tstatus: \"success\" | \"conflict\" | \"up-to-date\";\n\trebased: string[];\n\tconflictBranch?: string;\n}\n\n/**\n * Rebases all branches in the current stack onto their updated parents.\n *\n * Uses a snapshot-before-rebase strategy: captures every branch's tip\n * BEFORE starting any rebases, then uses `git rebase --onto <parent_new_tip>\n * <parent_old_tip> <child>`. This prevents the duplication bug where a child\n * replays its parent's already-rebased commits.\n *\n * On conflict, writes progress to `restack-progress.json` so\n * `dub restack --continue` can resume.\n *\n * @param cwd - Working directory\n * @returns Result with status, list of rebased branches, and optional conflict branch\n * @throws {DubError} If not initialized, dirty tree, not in a stack, or branch missing\n */\nexport async function restack(cwd: string): Promise<RestackResult> {\n\tconst state = await readState(cwd);\n\n\tif (!(await isWorkingTreeClean(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Working tree has uncommitted changes. Commit or stash them before restacking.\",\n\t\t);\n\t}\n\n\tconst originalBranch = await getCurrentBranch(cwd);\n\tconst targetStacks = getTargetStacks(state.stacks, originalBranch);\n\n\tif (targetStacks.length === 0) {\n\t\tthrow new DubError(\n\t\t\t`Branch '${originalBranch}' is not part of any stack. Run 'dub create' first.`,\n\t\t);\n\t}\n\n\tconst allBranches = targetStacks.flatMap((s) => s.branches);\n\tfor (const branch of allBranches) {\n\t\tif (!(await branchExists(branch.name, cwd))) {\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${branch.name}' is tracked in state but no longer exists in git.\\n` +\n\t\t\t\t\t\" Remove it from the stack or recreate it before restacking.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t// Snapshot all branch tips BEFORE building steps or rebasing\n\tconst branchTips: Record<string, string> = {};\n\tfor (const branch of allBranches) {\n\t\tbranchTips[branch.name] = await getBranchTip(branch.name, cwd);\n\t}\n\n\tconst steps = await buildRestackSteps(targetStacks, cwd);\n\n\tif (steps.length === 0) {\n\t\treturn { status: \"up-to-date\", rebased: [] };\n\t}\n\n\tawait saveUndoEntry(\n\t\t{\n\t\t\toperation: \"restack\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpreviousBranch: originalBranch,\n\t\t\tpreviousState: structuredClone(state),\n\t\t\tbranchTips,\n\t\t\tcreatedBranches: [],\n\t\t},\n\t\tcwd,\n\t);\n\n\tconst progress: RestackProgress = { originalBranch, steps };\n\tawait writeProgress(progress, cwd);\n\n\treturn executeRestackSteps(progress, cwd);\n}\n\n/**\n * Continues a restack after conflict resolution.\n *\n * Reads the saved progress file, finishes the in-progress rebase,\n * then resumes with remaining branches.\n *\n * @param cwd - Working directory\n * @throws {DubError} If no restack is in progress\n */\nexport async function restackContinue(cwd: string): Promise<RestackResult> {\n\tconst progress = await readProgress(cwd);\n\n\tif (!progress) {\n\t\tthrow new DubError(\"No restack in progress. Run 'dub restack' to start.\");\n\t}\n\n\tawait gitRebaseContinue(cwd);\n\n\tconst conflictedStep = progress.steps.find((s) => s.status === \"conflicted\");\n\tif (conflictedStep) {\n\t\tconflictedStep.status = \"done\";\n\t}\n\n\treturn executeRestackSteps(progress, cwd);\n}\n\nasync function executeRestackSteps(\n\tprogress: RestackProgress,\n\tcwd: string,\n): Promise<RestackResult> {\n\tconst rebased: string[] = [];\n\n\tfor (const step of progress.steps) {\n\t\tif (step.status !== \"pending\") {\n\t\t\tif (step.status === \"done\") rebased.push(step.branch);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst parentNewTip = await getBranchTip(step.parent, cwd);\n\t\tif (parentNewTip === step.parentOldTip) {\n\t\t\tstep.status = \"skipped\";\n\t\t\tawait writeProgress(progress, cwd);\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tawait rebaseOnto(parentNewTip, step.parentOldTip, step.branch, cwd);\n\t\t\tstep.status = \"done\";\n\t\t\trebased.push(step.branch);\n\t\t\tawait writeProgress(progress, cwd);\n\t\t} catch (error) {\n\t\t\tif (error instanceof DubError && error.message.includes(\"Conflict\")) {\n\t\t\t\tstep.status = \"conflicted\";\n\t\t\t\tawait writeProgress(progress, cwd);\n\t\t\t\treturn { status: \"conflict\", rebased, conflictBranch: step.branch };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tawait clearProgress(cwd);\n\tawait checkoutBranch(progress.originalBranch, cwd);\n\n\tconst allSkipped = progress.steps.every(\n\t\t(s) => s.status === \"skipped\" || s.status === \"done\",\n\t);\n\treturn {\n\t\tstatus: rebased.length === 0 && allSkipped ? \"up-to-date\" : \"success\",\n\t\trebased,\n\t};\n}\n\nfunction getTargetStacks(stacks: Stack[], currentBranch: string): Stack[] {\n\t// If current branch is a root of any stacks, restack all of them\n\tconst rootStacks = stacks.filter((s) =>\n\t\ts.branches.some((b) => b.name === currentBranch && b.type === \"root\"),\n\t);\n\tif (rootStacks.length > 0) return rootStacks;\n\n\t// Otherwise, find the stack containing the current branch\n\tconst stack = stacks.find((s) =>\n\t\ts.branches.some((b) => b.name === currentBranch),\n\t);\n\treturn stack ? [stack] : [];\n}\n\nasync function buildRestackSteps(\n\tstacks: Stack[],\n\tcwd: string,\n): Promise<RestackStep[]> {\n\tconst steps: RestackStep[] = [];\n\n\tfor (const stack of stacks) {\n\t\tconst ordered = topologicalOrder(stack);\n\t\tfor (const branch of ordered) {\n\t\t\tif (branch.type === \"root\" || !branch.parent) continue;\n\t\t\tconst mergeBase = await getMergeBase(branch.parent, branch.name, cwd);\n\t\t\tsteps.push({\n\t\t\t\tbranch: branch.name,\n\t\t\t\tparent: branch.parent,\n\t\t\t\tparentOldTip: mergeBase,\n\t\t\t\tstatus: \"pending\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn steps;\n}\n\nasync function getProgressPath(cwd: string): Promise<string> {\n\tconst dubDir = await getDubDir(cwd);\n\treturn path.join(dubDir, \"restack-progress.json\");\n}\n\nasync function writeProgress(\n\tprogress: RestackProgress,\n\tcwd: string,\n): Promise<void> {\n\tconst progressPath = await getProgressPath(cwd);\n\tfs.writeFileSync(progressPath, `${JSON.stringify(progress, null, 2)}\\n`);\n}\n\nasync function readProgress(cwd: string): Promise<RestackProgress | null> {\n\tconst progressPath = await getProgressPath(cwd);\n\tif (!fs.existsSync(progressPath)) return null;\n\tconst raw = fs.readFileSync(progressPath, \"utf-8\");\n\treturn JSON.parse(raw) as RestackProgress;\n}\n\nasync function clearProgress(cwd: string): Promise<void> {\n\tconst progressPath = await getProgressPath(cwd);\n\tif (fs.existsSync(progressPath)) {\n\t\tfs.unlinkSync(progressPath);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport { getCurrentBranch, getLastCommitMessage, pushBranch } from \"../lib/git\";\nimport {\n\tcheckGhAuth,\n\tcreatePr,\n\tensureGhInstalled,\n\tgetPr,\n\ttype PrInfo,\n\tupdatePrBody,\n} from \"../lib/github\";\nimport {\n\tbuildMetadataBlock,\n\tbuildStackTable,\n\tcomposePrBody,\n} from \"../lib/pr-body\";\nimport {\n\ttype Branch,\n\tfindStackForBranch,\n\treadState,\n\ttopologicalOrder,\n\twriteState,\n} from \"../lib/state\";\n\ninterface SubmitResult {\n\tpushed: string[];\n\tcreated: string[];\n\tupdated: string[];\n}\n\n/**\n * Pushes branches in the current stack and creates/updates GitHub PRs.\n *\n * @param cwd - Working directory\n * @param dryRun - If true, prints what would happen without executing\n * @throws {DubError} If not in a stack, on root branch, stack is non-linear, or gh errors\n */\nexport async function submit(\n\tcwd: string,\n\tdryRun: boolean,\n): Promise<SubmitResult> {\n\tawait ensureGhInstalled();\n\tawait checkGhAuth();\n\n\tconst state = await readState(cwd);\n\tconst currentBranch = await getCurrentBranch(cwd);\n\tconst stack = findStackForBranch(state, currentBranch);\n\n\tif (!stack) {\n\t\tthrow new DubError(\n\t\t\t`Branch '${currentBranch}' is not part of any stack. Run 'dub create' first.`,\n\t\t);\n\t}\n\n\tconst ordered = topologicalOrder(stack);\n\tconst currentEntry = ordered.find((b) => b.name === currentBranch);\n\tif (currentEntry?.type === \"root\") {\n\t\tthrow new DubError(\n\t\t\t\"Cannot submit from a root branch. Checkout a stack branch first.\",\n\t\t);\n\t}\n\n\tconst nonRootBranches = ordered.filter((b) => b.type !== \"root\");\n\n\tvalidateLinearStack(ordered);\n\n\tconst result: SubmitResult = { pushed: [], created: [], updated: [] };\n\tconst prMap = new Map<string, PrInfo>();\n\n\tfor (const branch of nonRootBranches) {\n\t\tif (dryRun) {\n\t\t\tconsole.log(`[dry-run] would push ${branch.name}`);\n\t\t} else {\n\t\t\tawait pushBranch(branch.name, cwd);\n\t\t}\n\t\tresult.pushed.push(branch.name);\n\t}\n\n\tfor (const branch of nonRootBranches) {\n\t\tconst base = branch.parent as string;\n\n\t\tif (dryRun) {\n\t\t\tconsole.log(`[dry-run] would check/create PR: ${branch.name} → ${base}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst existing = await getPr(branch.name, cwd);\n\t\tif (existing) {\n\t\t\tprMap.set(branch.name, existing);\n\t\t\tresult.updated.push(branch.name);\n\t\t} else {\n\t\t\tconst title = await getLastCommitMessage(branch.name, cwd);\n\t\t\tconst tmpFile = writeTempBody(\"\");\n\t\t\ttry {\n\t\t\t\tconst created = await createPr(branch.name, base, title, tmpFile, cwd);\n\t\t\t\tprMap.set(branch.name, created);\n\t\t\t\tresult.created.push(branch.name);\n\t\t\t} finally {\n\t\t\t\tcleanupTempFile(tmpFile);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!dryRun) {\n\t\tawait updateAllPrBodies(nonRootBranches, prMap, stack.id, cwd);\n\n\t\tfor (const branch of nonRootBranches) {\n\t\t\tconst pr = prMap.get(branch.name);\n\t\t\tif (pr) {\n\t\t\t\tconst stateBranch = stack.branches.find((b) => b.name === branch.name);\n\t\t\t\tif (stateBranch) {\n\t\t\t\t\tstateBranch.pr_number = pr.number;\n\t\t\t\t\tstateBranch.pr_link = pr.url;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tawait writeState(state, cwd);\n\t}\n\n\treturn result;\n}\n\nfunction validateLinearStack(ordered: Branch[]): void {\n\tconst childCount = new Map<string, number>();\n\tfor (const branch of ordered) {\n\t\tif (branch.parent) {\n\t\t\tchildCount.set(branch.parent, (childCount.get(branch.parent) ?? 0) + 1);\n\t\t}\n\t}\n\tfor (const [parent, count] of childCount) {\n\t\tif (count > 1) {\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${parent}' has ${count} children. ` +\n\t\t\t\t\t\"Branching stacks are not supported by submit. \" +\n\t\t\t\t\t\"Ensure each branch has at most one child.\",\n\t\t\t);\n\t\t}\n\t}\n}\n\nasync function updateAllPrBodies(\n\tbranches: Branch[],\n\tprMap: Map<string, PrInfo>,\n\tstackId: string,\n\tcwd: string,\n): Promise<void> {\n\tconst tableEntries = new Map<string, { number: number; title: string }>();\n\tfor (const branch of branches) {\n\t\tconst pr = prMap.get(branch.name);\n\t\tif (pr) {\n\t\t\ttableEntries.set(branch.name, { number: pr.number, title: pr.title });\n\t\t}\n\t}\n\n\tfor (let i = 0; i < branches.length; i++) {\n\t\tconst branch = branches[i];\n\t\tconst pr = prMap.get(branch.name);\n\t\tif (!pr) continue;\n\n\t\tconst prevPr =\n\t\t\ti > 0 ? (prMap.get(branches[i - 1].name)?.number ?? null) : null;\n\t\tconst nextPr =\n\t\t\ti < branches.length - 1\n\t\t\t\t? (prMap.get(branches[i + 1].name)?.number ?? null)\n\t\t\t\t: null;\n\n\t\tconst stackTable = buildStackTable(branches, tableEntries, branch.name);\n\t\tconst metadataBlock = buildMetadataBlock(\n\t\t\tstackId,\n\t\t\tpr.number,\n\t\t\tprevPr,\n\t\t\tnextPr,\n\t\t\tbranch.name,\n\t\t);\n\n\t\tconst existingBody = pr.body;\n\t\tconst finalBody = composePrBody(existingBody, stackTable, metadataBlock);\n\n\t\tconst tmpFile = writeTempBody(finalBody);\n\t\ttry {\n\t\t\tawait updatePrBody(pr.number, tmpFile, cwd);\n\t\t} finally {\n\t\t\tcleanupTempFile(tmpFile);\n\t\t}\n\t}\n}\n\nfunction writeTempBody(content: string): string {\n\tconst tmpDir = os.tmpdir();\n\tconst tmpFile = path.join(tmpDir, `dubstack-body-${Date.now()}.md`);\n\tfs.writeFileSync(tmpFile, content);\n\treturn tmpFile;\n}\n\nfunction cleanupTempFile(filePath: string): void {\n\ttry {\n\t\tfs.unlinkSync(filePath);\n\t} catch {\n\t\t// Best-effort cleanup\n\t}\n}\n","import { execa } from \"execa\";\nimport { DubError } from \"./errors\";\n\n/** Details of a GitHub Pull Request. */\nexport interface PrInfo {\n\tnumber: number;\n\turl: string;\n\ttitle: string;\n\tbody: string;\n}\n\n/**\n * Ensures the `gh` CLI is installed and available in PATH.\n * @throws {DubError} If `gh` is not found.\n */\nexport async function ensureGhInstalled(): Promise<void> {\n\ttry {\n\t\tawait execa(\"gh\", [\"--version\"]);\n\t} catch {\n\t\tthrow new DubError(\"gh CLI not found. Install it: https://cli.github.com\");\n\t}\n}\n\n/**\n * Ensures the user is authenticated with `gh`.\n * @throws {DubError} If not authenticated.\n */\nexport async function checkGhAuth(): Promise<void> {\n\ttry {\n\t\tawait execa(\"gh\", [\"auth\", \"status\"]);\n\t} catch {\n\t\tthrow new DubError(\"Not authenticated with GitHub. Run 'gh auth login'.\");\n\t}\n}\n\n/**\n * Fetches the open PR for a given head branch, if one exists.\n * @returns The PR info, or `null` if no open PR exists for that branch.\n */\nexport async function getPr(\n\tbranch: string,\n\tcwd: string,\n): Promise<PrInfo | null> {\n\tconst { stdout } = await execa(\n\t\t\"gh\",\n\t\t[\n\t\t\t\"pr\",\n\t\t\t\"list\",\n\t\t\t\"--head\",\n\t\t\tbranch,\n\t\t\t\"--state\",\n\t\t\t\"open\",\n\t\t\t\"--json\",\n\t\t\t\"number,url,title,body\",\n\t\t\t\"--jq\",\n\t\t\t\".[0]\",\n\t\t],\n\t\t{ cwd },\n\t);\n\n\tconst trimmed = stdout.trim();\n\tif (!trimmed || trimmed === \"null\") return null;\n\n\ttry {\n\t\treturn JSON.parse(trimmed) as PrInfo;\n\t} catch {\n\t\tthrow new DubError(`Failed to parse PR info for branch '${branch}'.`);\n\t}\n}\n\n/**\n * Creates a new PR and returns its info.\n *\n * Parses the PR number from the URL printed to stdout by `gh pr create`,\n * avoiding an extra API round-trip.\n *\n * @param branch - Head branch\n * @param base - Base branch the PR merges into\n * @param title - PR title\n * @param bodyFile - Absolute path to a file containing the PR body\n */\nexport async function createPr(\n\tbranch: string,\n\tbase: string,\n\ttitle: string,\n\tbodyFile: string,\n\tcwd: string,\n): Promise<PrInfo> {\n\tlet stdout: string;\n\ttry {\n\t\tconst result = await execa(\n\t\t\t\"gh\",\n\t\t\t[\n\t\t\t\t\"pr\",\n\t\t\t\t\"create\",\n\t\t\t\t\"--head\",\n\t\t\t\tbranch,\n\t\t\t\t\"--base\",\n\t\t\t\tbase,\n\t\t\t\t\"--title\",\n\t\t\t\ttitle,\n\t\t\t\t\"--body-file\",\n\t\t\t\tbodyFile,\n\t\t\t],\n\t\t\t{ cwd },\n\t\t);\n\t\tstdout = result.stdout;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"403\") || message.includes(\"insufficient\")) {\n\t\t\tthrow new DubError(\n\t\t\t\t\"GitHub token lacks required permissions. Run 'gh auth login' with the 'repo' scope.\",\n\t\t\t);\n\t\t}\n\t\tthrow new DubError(`Failed to create PR for '${branch}': ${message}`);\n\t}\n\n\tconst url = stdout.trim();\n\tconst numberMatch = url.match(/\\/pull\\/(\\d+)$/);\n\tif (!numberMatch) {\n\t\tthrow new DubError(`Unexpected output from 'gh pr create': ${url}`);\n\t}\n\n\treturn {\n\t\tnumber: Number.parseInt(numberMatch[1], 10),\n\t\turl,\n\t\ttitle,\n\t\tbody: \"\",\n\t};\n}\n\n/**\n * Updates a PR's body using a file.\n * @param prNumber - The PR number to update\n * @param bodyFile - Absolute path to a file containing the new body\n */\nexport async function updatePrBody(\n\tprNumber: number,\n\tbodyFile: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\n\t\t\t\"gh\",\n\t\t\t[\"pr\", \"edit\", String(prNumber), \"--body-file\", bodyFile],\n\t\t\t{ cwd },\n\t\t);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"403\") || message.includes(\"insufficient\")) {\n\t\t\tthrow new DubError(\n\t\t\t\t\"GitHub token lacks required permissions. Run 'gh auth login' with the 'repo' scope.\",\n\t\t\t);\n\t\t}\n\t\tthrow new DubError(`Failed to update PR #${prNumber}: ${message}`);\n\t}\n}\n","import type { Branch } from \"./state\";\n\ninterface StackEntry {\n\tnumber: number;\n\ttitle: string;\n}\n\nconst DUBSTACK_START = \"<!-- dubstack:start -->\";\nconst DUBSTACK_END = \"<!-- dubstack:end -->\";\nconst METADATA_START = \"<!-- dubstack-metadata\";\nconst METADATA_END = \"-->\";\n\n/**\n * Builds the visible stack navigation table wrapped in dubstack markers.\n *\n * @param orderedBranches - Non-root branches in topological order\n * @param prMap - Map of branch name → PR number + title\n * @param currentBranch - The branch to mark with 👈\n */\nexport function buildStackTable(\n\torderedBranches: Branch[],\n\tprMap: Map<string, StackEntry>,\n\tcurrentBranch: string,\n): string {\n\tconst lines = orderedBranches.map((branch) => {\n\t\tconst entry = prMap.get(branch.name);\n\t\tif (!entry) return `- ${branch.name}`;\n\t\tconst marker = branch.name === currentBranch ? \" 👈\" : \"\";\n\t\treturn `- #${entry.number} ${entry.title}${marker}`;\n\t});\n\n\treturn [\n\t\tDUBSTACK_START,\n\t\t\"---\",\n\t\t\"### 🥞 DubStack\",\n\t\t...lines,\n\t\tDUBSTACK_END,\n\t].join(\"\\n\");\n}\n\n/**\n * Builds the hidden metadata HTML comment block.\n */\nexport function buildMetadataBlock(\n\tstackId: string,\n\tprNumber: number,\n\tprevPr: number | null,\n\tnextPr: number | null,\n\tbranch: string,\n): string {\n\tconst metadata = {\n\t\tstack_id: stackId,\n\t\tpr_number: prNumber,\n\t\tprev_pr: prevPr,\n\t\tnext_pr: nextPr,\n\t\tbranch,\n\t};\n\treturn `${METADATA_START}\\n${JSON.stringify(metadata, null, 2)}\\n${METADATA_END}`;\n}\n\n/**\n * Strips all DubStack-generated sections from a PR body.\n * Preserves user-written content. Returns body unchanged if no markers exist.\n */\nexport function stripDubstackSections(body: string): string {\n\tlet result = body;\n\n\tconst startIdx = result.indexOf(DUBSTACK_START);\n\tconst endIdx = result.indexOf(DUBSTACK_END);\n\tif (startIdx !== -1 && endIdx !== -1) {\n\t\tresult =\n\t\t\tresult.slice(0, startIdx) + result.slice(endIdx + DUBSTACK_END.length);\n\t}\n\n\tconst metaStart = result.indexOf(METADATA_START);\n\tif (metaStart !== -1) {\n\t\tconst metaEnd = result.indexOf(\n\t\t\tMETADATA_END,\n\t\t\tmetaStart + METADATA_START.length,\n\t\t);\n\t\tif (metaEnd !== -1) {\n\t\t\tresult =\n\t\t\t\tresult.slice(0, metaStart) +\n\t\t\t\tresult.slice(metaEnd + METADATA_END.length);\n\t\t}\n\t}\n\n\treturn result.trimEnd();\n}\n\n/**\n * Composes the final PR body by combining user content with DubStack sections.\n *\n * @param existingBody - The existing PR body (may contain stale DubStack sections)\n * @param stackTable - Output of `buildStackTable`\n * @param metadataBlock - Output of `buildMetadataBlock`\n */\nexport function composePrBody(\n\texistingBody: string,\n\tstackTable: string,\n\tmetadataBlock: string,\n): string {\n\tconst userContent = stripDubstackSections(existingBody);\n\tconst parts = [userContent, stackTable, metadataBlock].filter(Boolean);\n\treturn parts.join(\"\\n\\n\");\n}\n","import { DubError } from \"../lib/errors\";\nimport {\n\tcheckoutBranch,\n\tdeleteBranch,\n\tforceBranchTo,\n\tgetCurrentBranch,\n\tisWorkingTreeClean,\n} from \"../lib/git\";\nimport { writeState } from \"../lib/state\";\nimport { clearUndoEntry, readUndoEntry } from \"../lib/undo-log\";\n\ninterface UndoResult {\n\tundone: \"create\" | \"restack\";\n\tdetails: string;\n}\n\n/**\n * Undoes the last `dub create` or `dub restack` operation.\n *\n * Reversal strategy:\n * - **create**: Deletes the created branch, restores state, checks out the previous branch.\n * - **restack**: Resets every rebased branch to its pre-rebase tip via `git branch -f`,\n * restores state, checks out the previous branch.\n *\n * Only one level of undo is supported. After undo, the undo entry is cleared.\n *\n * @param cwd - Working directory\n * @returns What was undone and a human-readable summary\n * @throws {DubError} If nothing to undo or working tree is dirty\n */\nexport async function undo(cwd: string): Promise<UndoResult> {\n\tconst entry = await readUndoEntry(cwd);\n\n\tif (!(await isWorkingTreeClean(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Working tree has uncommitted changes. Commit or stash them before undoing.\",\n\t\t);\n\t}\n\n\tconst currentBranch = await getCurrentBranch(cwd);\n\n\tif (entry.operation === \"create\") {\n\t\t// If we're on a branch that's about to be deleted, switch away first\n\t\tconst needsCheckout = entry.createdBranches.includes(currentBranch);\n\t\tif (needsCheckout) {\n\t\t\tawait checkoutBranch(entry.previousBranch, cwd);\n\t\t}\n\n\t\tfor (const branch of entry.createdBranches) {\n\t\t\tawait deleteBranch(branch, cwd);\n\t\t}\n\n\t\tif (!needsCheckout && currentBranch !== entry.previousBranch) {\n\t\t\tawait checkoutBranch(entry.previousBranch, cwd);\n\t\t}\n\n\t\tawait writeState(entry.previousState, cwd);\n\t\tawait clearUndoEntry(cwd);\n\n\t\treturn {\n\t\t\tundone: \"create\",\n\t\t\tdetails: `Deleted branch${entry.createdBranches.length > 1 ? \"es\" : \"\"} '${entry.createdBranches.join(\"', '\")}'`,\n\t\t};\n\t}\n\n\t// restack undo: reset all branches to their pre-rebase tips\n\t// First checkout a safe branch so we don't conflict with force-moves\n\tawait checkoutBranch(entry.previousBranch, cwd);\n\n\tfor (const [name, sha] of Object.entries(entry.branchTips)) {\n\t\tif (name === entry.previousBranch) continue; // skip the branch we're on\n\t\tawait forceBranchTo(name, sha, cwd);\n\t}\n\n\t// Now force the branch we're on (if it was tracked)\n\tif (entry.branchTips[entry.previousBranch]) {\n\t\tawait forceBranchTo(\n\t\t\tentry.previousBranch,\n\t\t\tentry.branchTips[entry.previousBranch],\n\t\t\tcwd,\n\t\t);\n\t}\n\n\tawait writeState(entry.previousState, cwd);\n\tawait clearUndoEntry(cwd);\n\n\treturn {\n\t\tundone: \"restack\",\n\t\tdetails: `Reset ${Object.keys(entry.branchTips).length} branches to pre-restack state`,\n\t};\n}\n"],"mappings":"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","names":["execa","init_skills","chalk","fs","path","fs","path","fs","path","fs","path","execa","require","chalk","addSkills","removeSkills"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dubstack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI tool for managing stacked diffs (dependent git branches)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"@inquirer/search": "^4.1.3",
|
|
30
31
|
"chalk": "^5.6.2",
|
|
31
32
|
"commander": "^14.0.3",
|
|
32
33
|
"execa": "^9.6.1"
|