@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 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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@skippercorp/skipper",
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "publishConfig": {
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(repoPath: string, worktreePath: string): Promise<void> {
110
- const result = await Bun.$`git -C ${repoPath} worktree remove ${worktreePath}`.nothrow();
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 fallback = `git worktree remove failed with code ${result.exitCode}`;
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