@henryqw/pi-herdr-clone 0.1.2 → 0.1.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.
@@ -6,7 +6,12 @@ import {
6
6
  SessionManager,
7
7
  type ExtensionAPI,
8
8
  } from "@earendil-works/pi-coding-agent";
9
- import { createHerdrClient, herdrCommandFailure, hasHerdrErrorCode } from "@henryqw/pi-herdr";
9
+ import { createHerdrClient, herdrCommandFailure, hasHerdrErrorCode, withWorktreeLock, type HerdrExecResult } from "@henryqw/pi-herdr";
10
+
11
+ type WorkspaceInfo = {
12
+ workspace_id?: unknown;
13
+ worktree?: { checkout_path?: unknown } | null;
14
+ };
10
15
 
11
16
  const errorMessage = (error: unknown) => error instanceof Error ? error.message : String(error);
12
17
 
@@ -46,34 +51,44 @@ export default function herdrCloneExtension(pi: ExtensionAPI): void {
46
51
  const pane = (paneResponse as { result?: { pane?: { pane_id?: unknown; workspace_id?: unknown } } }).result?.pane;
47
52
  requiredString(pane?.pane_id, "Herdr pane response pane_id");
48
53
  const workspaceId = requiredString(pane?.workspace_id, "Herdr pane response workspace_id");
49
-
50
- const session = SessionManager.open(sessionFile, ctx.sessionManager.getSessionDir(), ctx.cwd);
51
- const createdClone = session.createBranchedSession(leafId);
52
- if (!createdClone) throw new Error("Pi did not create a persisted clone session file.");
53
- const cloneFile = resolve(createdClone);
54
- let cloneStat;
55
- try {
56
- cloneStat = await stat(cloneFile);
57
- } catch (error) {
58
- throw new Error(`Pi clone session file was not created: ${cloneFile}`, { cause: error });
54
+ const workspaceResponse = await herdr.json(["workspace", "get", workspaceId], { cwd: ctx.cwd });
55
+ const workspace = (workspaceResponse as { result?: { workspace?: WorkspaceInfo } }).result?.workspace;
56
+ if (requiredString(workspace?.workspace_id, "Herdr workspace response workspace_id") !== workspaceId) {
57
+ throw new Error(`Herdr workspace response did not match ${workspaceId}.`);
59
58
  }
60
- if (!cloneStat.isFile()) throw new Error(`Pi clone session path is not a file: ${cloneFile}`);
61
-
62
- const tabCreateArgs = ["tab", "create", "--workspace", workspaceId, "--cwd", ctx.cwd, "--no-focus"] as const;
63
- const createdTab = await herdr.exec(tabCreateArgs, { cwd: ctx.cwd });
64
- if (createdTab.code !== 0 || createdTab.killed) {
65
- const createError = new Error(herdrCommandFailure(tabCreateArgs, createdTab));
59
+ const checkout = workspace?.worktree == null
60
+ ? undefined
61
+ : requiredString(workspace.worktree.checkout_path, "Herdr workspace response checkout_path");
62
+ const mutate = async (): Promise<{ createdTab: HerdrExecResult; cloneFile: string }> => {
63
+ const session = SessionManager.open(sessionFile, ctx.sessionManager.getSessionDir(), ctx.cwd);
64
+ const createdClone = session.createBranchedSession(leafId);
65
+ if (!createdClone) throw new Error("Pi did not create a persisted clone session file.");
66
+ const cloneFile = resolve(createdClone);
67
+ let cloneStat;
66
68
  try {
67
- await unlink(cloneFile);
68
- } catch (cleanupError) {
69
- throw new AggregateError(
70
- [createError, cleanupError],
71
- `${createError.message} Clone cleanup also failed for ${cloneFile}: ${errorMessage(cleanupError)}`,
72
- );
69
+ cloneStat = await stat(cloneFile);
70
+ } catch (error) {
71
+ throw new Error(`Pi clone session file was not created: ${cloneFile}`, { cause: error });
73
72
  }
74
- throw createError;
75
- }
73
+ if (!cloneStat.isFile()) throw new Error(`Pi clone session path is not a file: ${cloneFile}`);
76
74
 
75
+ const tabCreateArgs = ["tab", "create", "--workspace", workspaceId, "--cwd", ctx.cwd, "--no-focus"] as const;
76
+ const createdTab = await herdr.exec(tabCreateArgs, { cwd: ctx.cwd });
77
+ if (createdTab.code !== 0 || createdTab.killed) {
78
+ const createError = new Error(herdrCommandFailure(tabCreateArgs, createdTab));
79
+ try {
80
+ await unlink(cloneFile);
81
+ } catch (cleanupError) {
82
+ throw new AggregateError(
83
+ [createError, cleanupError],
84
+ `${createError.message} Clone cleanup also failed for ${cloneFile}: ${errorMessage(cleanupError)}`,
85
+ );
86
+ }
87
+ throw createError;
88
+ }
89
+ return { createdTab, cloneFile };
90
+ };
91
+ const { createdTab, cloneFile } = checkout ? await withWorktreeLock(checkout, mutate) : await mutate();
77
92
  let tabId: string | undefined;
78
93
  let rootPaneId: string | undefined;
79
94
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-herdr-clone",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Clone the current Pi conversation path into a new Herdr tab.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -28,7 +28,7 @@
28
28
  "@earendil-works/pi-coding-agent": "^0.84.2"
29
29
  },
30
30
  "dependencies": {
31
- "@henryqw/pi-herdr": "^0.1.1"
31
+ "@henryqw/pi-herdr": "^0.2.0"
32
32
  },
33
33
  "repository": {
34
34
  "type": "git",