@skippercorp/skipper 1.0.2 → 1.0.3
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 +1 -1
- package/package.json +1 -1
- package/src/command/rm.ts +20 -7
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ Skipper manages:
|
|
|
81
81
|
| --- | --- |
|
|
82
82
|
| `skipper clone <owner/repo-or-url>` | Clones repo using `gh` into `~/.local/share/github` |
|
|
83
83
|
| `skipper a` | Selects repo/worktree with `fzf`, creates if missing, then attaches tmux |
|
|
84
|
-
| `skipper rm` | Removes selected worktree and kills matching tmux session |
|
|
84
|
+
| `skipper rm [--force]` | Removes selected worktree and kills matching tmux session (`--force` discards local worktree changes) |
|
|
85
85
|
| `skipper run "<prompt>"` | Selects repo, pulls latest, runs `opencode run` |
|
|
86
86
|
| `skipper aws bootstrap ...` | Deploys shared AWS ingress stack + optional GitHub webhook |
|
|
87
87
|
| `skipper aws deploy ...` | Deploys repository-scoped subscription stack |
|
package/package.json
CHANGED
package/src/command/rm.ts
CHANGED
|
@@ -11,6 +11,10 @@ type WorktreeRef = {
|
|
|
11
11
|
path: string;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
type RemoveCommandOptions = {
|
|
15
|
+
force?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
14
18
|
/**
|
|
15
19
|
* Register remove worktree command.
|
|
16
20
|
*
|
|
@@ -21,6 +25,7 @@ export function registerRemoveCommand(program: Command): void {
|
|
|
21
25
|
program
|
|
22
26
|
.command("rm")
|
|
23
27
|
.description("Remove a worktree (repo+worktree)")
|
|
28
|
+
.option("-f, --force", "Force remove worktree with uncommitted changes")
|
|
24
29
|
.action(runRemoveCommand);
|
|
25
30
|
}
|
|
26
31
|
|
|
@@ -30,7 +35,8 @@ export function registerRemoveCommand(program: Command): void {
|
|
|
30
35
|
* @since 1.0.0
|
|
31
36
|
* @category Worktree
|
|
32
37
|
*/
|
|
33
|
-
async function runRemoveCommand(): Promise<void> {
|
|
38
|
+
async function runRemoveCommand(options: RemoveCommandOptions = {}): Promise<void> {
|
|
39
|
+
const force = options.force === true;
|
|
34
40
|
const worktreeBaseDir = `${process.env.HOME}/.local/share/skipper/worktree`;
|
|
35
41
|
const allWorktrees = await collectWorktrees(worktreeBaseDir);
|
|
36
42
|
assertNonEmpty(allWorktrees, "No worktrees found");
|
|
@@ -39,7 +45,7 @@ async function runRemoveCommand(): Promise<void> {
|
|
|
39
45
|
console.log("No worktree selected");
|
|
40
46
|
process.exit(0);
|
|
41
47
|
}
|
|
42
|
-
await removeWorktree(selected);
|
|
48
|
+
await removeWorktree(selected, force);
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
/**
|
|
@@ -85,13 +91,13 @@ async function selectWorktree(worktrees: WorktreeRef[]): Promise<WorktreeRef | u
|
|
|
85
91
|
* @since 1.0.0
|
|
86
92
|
* @category Worktree
|
|
87
93
|
*/
|
|
88
|
-
async function removeWorktree(target: WorktreeRef): Promise<void> {
|
|
94
|
+
async function removeWorktree(target: WorktreeRef, force: boolean): Promise<void> {
|
|
89
95
|
const githubDir = `${process.env.HOME}/.local/share/github`;
|
|
90
96
|
const repoPath = `${githubDir}/${target.repo}`;
|
|
91
97
|
const sessionName = `${target.repo}-${target.worktree}`;
|
|
92
98
|
const name = `${target.repo}/${target.worktree}`;
|
|
93
99
|
console.log(`Removing worktree: ${name}`);
|
|
94
|
-
await removeGitWorktree(repoPath, target.path);
|
|
100
|
+
await removeGitWorktree(repoPath, target.path, force);
|
|
95
101
|
await Bun.$`rm -rf ${target.path}`;
|
|
96
102
|
if (await tmuxSessionExists(sessionName)) {
|
|
97
103
|
await Bun.$`tmux kill-session -t ${sessionName}`;
|
|
@@ -106,11 +112,18 @@ async function removeWorktree(target: WorktreeRef): Promise<void> {
|
|
|
106
112
|
* @since 1.0.0
|
|
107
113
|
* @category Worktree
|
|
108
114
|
*/
|
|
109
|
-
async function removeGitWorktree(
|
|
110
|
-
|
|
115
|
+
async function removeGitWorktree(
|
|
116
|
+
repoPath: string,
|
|
117
|
+
worktreePath: string,
|
|
118
|
+
force: boolean,
|
|
119
|
+
): Promise<void> {
|
|
120
|
+
const result = force
|
|
121
|
+
? await Bun.$`git -C ${repoPath} worktree remove --force ${worktreePath}`.nothrow()
|
|
122
|
+
: await Bun.$`git -C ${repoPath} worktree remove ${worktreePath}`.nothrow();
|
|
111
123
|
if (result.exitCode === 0) return;
|
|
112
124
|
const stderr = result.stderr.toString().trim();
|
|
113
|
-
const
|
|
125
|
+
const command = force ? "git worktree remove --force" : "git worktree remove";
|
|
126
|
+
const fallback = `${command} failed with code ${result.exitCode}`;
|
|
114
127
|
throw new Error(stderr || fallback);
|
|
115
128
|
}
|
|
116
129
|
|