@henryqw/pi-herdr-done 0.3.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 +2 -2
- package/extensions/done.ts +25 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ pi install npm:@henryqw/pi-herdr-done
|
|
|
13
13
|
| Surface | Type | Purpose |
|
|
14
14
|
| --- | --- | --- |
|
|
15
15
|
| `/done` | command | Remove the current worktree checkout, fast-forward the parent workspace, and close only this session's tab. |
|
|
16
|
-
| `/done --force` | command | Same, even when the worktree is dirty. |
|
|
16
|
+
| `/done --force` | command | Same, even when the worktree is dirty or used by other Herdr tabs. |
|
|
17
17
|
|
|
18
18
|
Both forms wait for Pi to become idle. `/done` asks for confirmation first; `/done --force` skips it because the flag already states intent. Normal removal runs:
|
|
19
19
|
|
|
@@ -25,7 +25,7 @@ herdr tab close "$HERDR_TAB_ID"
|
|
|
25
25
|
|
|
26
26
|
The parent pull runs only when this session ran in a linked worktree with a non-bare primary; it is skipped when the parent has diverged (`--ff-only` fails safely) or when another Herdr tab is working in the parent. Concurrent completions serialize on a lock around the parent checkout.
|
|
27
27
|
|
|
28
|
-
Only the current session's tab closes, so sibling tabs in the same workspace survive. Command requires Pi running inside Herdr with `HERDR_ENV=1` and `HERDR_TAB_ID` set.
|
|
28
|
+
Only the current session's tab closes, so sibling tabs in the same workspace survive. When another Herdr tab still uses the checkout or its parent, `/done` refuses and lists the blocking tabs by name; use `/done --force` to remove the checkout regardless of other tabs. Command requires Pi running inside Herdr with `HERDR_ENV=1` and `HERDR_TAB_ID` set.
|
|
29
29
|
|
|
30
30
|
Dirty worktrees make `/done` fail. Commit or discard changes, or use `/done --force` to explicitly delete them.
|
|
31
31
|
|
package/extensions/done.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { lock } from "proper-lockfile";
|
|
|
6
6
|
type ExecResult = { stdout: string; stderr: string; code: number; killed?: boolean };
|
|
7
7
|
|
|
8
8
|
type SnapshotPane = { tab_id?: unknown; cwd?: unknown };
|
|
9
|
+
type TabEntry = { tab_id?: unknown; label?: unknown };
|
|
9
10
|
|
|
10
11
|
export default function herdrDoneExtension(pi: ExtensionAPI): void {
|
|
11
12
|
const herdr = createHerdrClient<{ cwd: string }>((command, args, options) =>
|
|
@@ -48,19 +49,30 @@ export default function herdrDoneExtension(pi: ExtensionAPI): void {
|
|
|
48
49
|
|
|
49
50
|
const release = await lock(checkout);
|
|
50
51
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
guarded
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
// With --force, skip the dependents check entirely and let git remove the checkout.
|
|
53
|
+
if (option !== "--force") {
|
|
54
|
+
const snapshot = await herdr.json(["api", "snapshot"], { cwd: ctx.cwd });
|
|
55
|
+
const panes = (snapshot.result as { snapshot?: { panes?: SnapshotPane[] } } | undefined)?.snapshot?.panes;
|
|
56
|
+
if (!Array.isArray(panes)) throw new Error("herdr api snapshot returned no panes.");
|
|
57
|
+
const guarded = mainCheckout === checkout ? [checkout] : [checkout, mainCheckout];
|
|
58
|
+
const isGuarded = (cwd: string) =>
|
|
59
|
+
guarded.some((root) => cwd === root || cwd.startsWith(`${root}/`));
|
|
60
|
+
const dependentIds = [...new Set(panes
|
|
61
|
+
.filter((pane): pane is SnapshotPane & { tab_id: string; cwd: string } =>
|
|
62
|
+
typeof pane.tab_id === "string" && pane.tab_id !== tabId &&
|
|
63
|
+
typeof pane.cwd === "string" && isGuarded(pane.cwd))
|
|
64
|
+
.map((pane) => pane.tab_id))];
|
|
65
|
+
if (dependentIds.length > 0) {
|
|
66
|
+
const listing = await herdr.json(["tab", "list"], { cwd: ctx.cwd });
|
|
67
|
+
const tabs = (listing.result as { tabs?: TabEntry[] } | undefined)?.tabs;
|
|
68
|
+
const labels = new Map(
|
|
69
|
+
(Array.isArray(tabs) ? tabs : [])
|
|
70
|
+
.filter((tab): tab is TabEntry & { tab_id: string; label: string } =>
|
|
71
|
+
typeof tab.tab_id === "string" && typeof tab.label === "string")
|
|
72
|
+
.map((tab) => [tab.tab_id, tab.label]));
|
|
73
|
+
const names = [...new Set(dependentIds.map((id) => labels.get(id) ?? id))];
|
|
74
|
+
throw new Error(`Worktree still used by Herdr tabs ${names.join(", ")}; close them first.`);
|
|
75
|
+
}
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
await execOrThrow("git", [
|