@agimon-ai/doompi-git 0.0.1-alpha.3 → 0.0.1-alpha.31

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.
Files changed (180) hide show
  1. package/README.md +12 -0
  2. package/dist/apiContracts.cjs +198 -0
  3. package/dist/apiContracts.cjs.map +1 -0
  4. package/dist/apiContracts.d.mts +53 -0
  5. package/dist/apiContracts.d.mts.map +1 -0
  6. package/dist/apiContracts.mjs +193 -0
  7. package/dist/apiContracts.mjs.map +1 -0
  8. package/dist/extensions/pi.cjs +105 -1
  9. package/dist/extensions/pi.cjs.map +1 -0
  10. package/dist/extensions/pi.d.mts +6 -2
  11. package/dist/extensions/pi.d.mts.map +1 -0
  12. package/dist/extensions/pi.mjs +100 -1
  13. package/dist/extensions/pi.mjs.map +1 -0
  14. package/dist/extensions/server.cjs +588 -0
  15. package/dist/extensions/server.cjs.map +1 -0
  16. package/dist/extensions/server.d.mts +5 -0
  17. package/dist/extensions/server.d.mts.map +1 -0
  18. package/dist/extensions/server.mjs +583 -0
  19. package/dist/extensions/server.mjs.map +1 -0
  20. package/dist/extensions/web.mjs +911 -0
  21. package/dist/extensions/web.mjs.map +1 -0
  22. package/dist/gitDependencies-CJFmhrG1.mjs +22 -0
  23. package/dist/gitDependencies-CJFmhrG1.mjs.map +1 -0
  24. package/dist/gitDependencies-CWsygnLQ.cjs +33 -0
  25. package/dist/gitDependencies-CWsygnLQ.cjs.map +1 -0
  26. package/dist/index-D79MSCee.d.mts +145 -0
  27. package/dist/index-D79MSCee.d.mts.map +1 -0
  28. package/dist/index.cjs +4 -1
  29. package/dist/index.d.mts +2 -5
  30. package/dist/index.mjs +2 -1
  31. package/dist/runWorktree-BdK3oRtX.mjs +298 -0
  32. package/dist/runWorktree-BdK3oRtX.mjs.map +1 -0
  33. package/dist/runWorktree-D0bsyYc0.cjs +351 -0
  34. package/dist/runWorktree-D0bsyYc0.cjs.map +1 -0
  35. package/dist/worktreeOperations-CijBzYEf.mjs +1163 -0
  36. package/dist/worktreeOperations-CijBzYEf.mjs.map +1 -0
  37. package/dist/worktreeOperations-FJDk44Zt.cjs +1253 -0
  38. package/dist/worktreeOperations-FJDk44Zt.cjs.map +1 -0
  39. package/llms.txt +4 -0
  40. package/package.json +54 -39
  41. package/src/prompts/doompi-use-git/SKILL.md +26 -3
  42. package/dist/_virtual/_rolldown/runtime.cjs +0 -1
  43. package/dist/adapters/channel/worktreeChannel.cjs +0 -2
  44. package/dist/adapters/channel/worktreeChannel.cjs.map +0 -1
  45. package/dist/adapters/channel/worktreeChannel.d.cts +0 -40
  46. package/dist/adapters/channel/worktreeChannel.d.cts.map +0 -1
  47. package/dist/adapters/channel/worktreeChannel.d.mts +0 -40
  48. package/dist/adapters/channel/worktreeChannel.d.mts.map +0 -1
  49. package/dist/adapters/channel/worktreeChannel.mjs +0 -2
  50. package/dist/adapters/channel/worktreeChannel.mjs.map +0 -1
  51. package/dist/adapters/filesystem/atomicJson.cjs +0 -2
  52. package/dist/adapters/filesystem/atomicJson.cjs.map +0 -1
  53. package/dist/adapters/filesystem/atomicJson.mjs +0 -2
  54. package/dist/adapters/filesystem/atomicJson.mjs.map +0 -1
  55. package/dist/adapters/filesystem/paths.cjs +0 -2
  56. package/dist/adapters/filesystem/paths.cjs.map +0 -1
  57. package/dist/adapters/filesystem/paths.mjs +0 -2
  58. package/dist/adapters/filesystem/paths.mjs.map +0 -1
  59. package/dist/adapters/hub/hubClient.cjs +0 -2
  60. package/dist/adapters/hub/hubClient.cjs.map +0 -1
  61. package/dist/adapters/hub/hubClient.d.cts +0 -1
  62. package/dist/adapters/hub/hubClient.d.mts +0 -1
  63. package/dist/adapters/hub/hubClient.mjs +0 -2
  64. package/dist/adapters/hub/hubClient.mjs.map +0 -1
  65. package/dist/adapters/pi/extension.cjs +0 -2
  66. package/dist/adapters/pi/extension.cjs.map +0 -1
  67. package/dist/adapters/pi/extension.d.cts +0 -9
  68. package/dist/adapters/pi/extension.d.cts.map +0 -1
  69. package/dist/adapters/pi/extension.d.mts +0 -9
  70. package/dist/adapters/pi/extension.d.mts.map +0 -1
  71. package/dist/adapters/pi/extension.mjs +0 -2
  72. package/dist/adapters/pi/extension.mjs.map +0 -1
  73. package/dist/adapters/pi/extensions/runWorktreeTool.cjs +0 -15
  74. package/dist/adapters/pi/extensions/runWorktreeTool.cjs.map +0 -1
  75. package/dist/adapters/pi/extensions/runWorktreeTool.mjs +0 -15
  76. package/dist/adapters/pi/extensions/runWorktreeTool.mjs.map +0 -1
  77. package/dist/adapters/web/worktreesChannel.cjs +0 -2
  78. package/dist/adapters/web/worktreesChannel.cjs.map +0 -1
  79. package/dist/adapters/web/worktreesChannel.d.cts +0 -21
  80. package/dist/adapters/web/worktreesChannel.d.cts.map +0 -1
  81. package/dist/adapters/web/worktreesChannel.d.mts +0 -21
  82. package/dist/adapters/web/worktreesChannel.d.mts.map +0 -1
  83. package/dist/adapters/web/worktreesChannel.mjs +0 -2
  84. package/dist/adapters/web/worktreesChannel.mjs.map +0 -1
  85. package/dist/adapters/worktree/dependencyInstall.cjs +0 -2
  86. package/dist/adapters/worktree/dependencyInstall.cjs.map +0 -1
  87. package/dist/adapters/worktree/dependencyInstall.mjs +0 -2
  88. package/dist/adapters/worktree/dependencyInstall.mjs.map +0 -1
  89. package/dist/adapters/worktree/gitCli.cjs +0 -4
  90. package/dist/adapters/worktree/gitCli.cjs.map +0 -1
  91. package/dist/adapters/worktree/gitCli.mjs +0 -4
  92. package/dist/adapters/worktree/gitCli.mjs.map +0 -1
  93. package/dist/adapters/worktree/repositoryIdentity.cjs +0 -2
  94. package/dist/adapters/worktree/repositoryIdentity.cjs.map +0 -1
  95. package/dist/adapters/worktree/repositoryIdentity.mjs +0 -2
  96. package/dist/adapters/worktree/repositoryIdentity.mjs.map +0 -1
  97. package/dist/adapters/worktree/worktreeOperations.cjs +0 -2
  98. package/dist/adapters/worktree/worktreeOperations.cjs.map +0 -1
  99. package/dist/adapters/worktree/worktreeOperations.d.cts +0 -32
  100. package/dist/adapters/worktree/worktreeOperations.d.cts.map +0 -1
  101. package/dist/adapters/worktree/worktreeOperations.d.mts +0 -32
  102. package/dist/adapters/worktree/worktreeOperations.d.mts.map +0 -1
  103. package/dist/adapters/worktree/worktreeOperations.mjs +0 -2
  104. package/dist/adapters/worktree/worktreeOperations.mjs.map +0 -1
  105. package/dist/adapters/worktree/worktreeRegistry.cjs +0 -2
  106. package/dist/adapters/worktree/worktreeRegistry.cjs.map +0 -1
  107. package/dist/adapters/worktree/worktreeRegistry.mjs +0 -2
  108. package/dist/adapters/worktree/worktreeRegistry.mjs.map +0 -1
  109. package/dist/commands/doomGitCommand.cjs +0 -2
  110. package/dist/commands/doomGitCommand.cjs.map +0 -1
  111. package/dist/commands/doomGitCommand.mjs +0 -2
  112. package/dist/commands/doomGitCommand.mjs.map +0 -1
  113. package/dist/container/index.cjs +0 -2
  114. package/dist/container/index.cjs.map +0 -1
  115. package/dist/container/index.d.cts +0 -6
  116. package/dist/container/index.d.cts.map +0 -1
  117. package/dist/container/index.d.mts +0 -6
  118. package/dist/container/index.d.mts.map +0 -1
  119. package/dist/container/index.mjs +0 -2
  120. package/dist/container/index.mjs.map +0 -1
  121. package/dist/extensions/pi.d.cts +0 -2
  122. package/dist/hubApi.cjs +0 -2
  123. package/dist/hubApi.cjs.map +0 -1
  124. package/dist/hubApi.d.cts +0 -6
  125. package/dist/hubApi.d.cts.map +0 -1
  126. package/dist/hubApi.d.mts +0 -6
  127. package/dist/hubApi.d.mts.map +0 -1
  128. package/dist/hubApi.mjs +0 -2
  129. package/dist/hubApi.mjs.map +0 -1
  130. package/dist/index.d.cts +0 -5
  131. package/dist/schemas/runWorktreeTool.cjs +0 -2
  132. package/dist/schemas/runWorktreeTool.cjs.map +0 -1
  133. package/dist/schemas/runWorktreeTool.mjs +0 -2
  134. package/dist/schemas/runWorktreeTool.mjs.map +0 -1
  135. package/dist/services/extensionService.cjs +0 -2
  136. package/dist/services/extensionService.cjs.map +0 -1
  137. package/dist/services/extensionService.d.cts +0 -8
  138. package/dist/services/extensionService.d.cts.map +0 -1
  139. package/dist/services/extensionService.d.mts +0 -8
  140. package/dist/services/extensionService.d.mts.map +0 -1
  141. package/dist/services/extensionService.mjs +0 -2
  142. package/dist/services/extensionService.mjs.map +0 -1
  143. package/dist/services/support/errors.cjs +0 -2
  144. package/dist/services/support/errors.cjs.map +0 -1
  145. package/dist/services/support/errors.mjs +0 -2
  146. package/dist/services/support/errors.mjs.map +0 -1
  147. package/dist/services/worktreeNaming.cjs +0 -2
  148. package/dist/services/worktreeNaming.cjs.map +0 -1
  149. package/dist/services/worktreeNaming.d.cts +0 -27
  150. package/dist/services/worktreeNaming.d.cts.map +0 -1
  151. package/dist/services/worktreeNaming.d.mts +0 -27
  152. package/dist/services/worktreeNaming.d.mts.map +0 -1
  153. package/dist/services/worktreeNaming.mjs +0 -2
  154. package/dist/services/worktreeNaming.mjs.map +0 -1
  155. package/dist/types/extension.d.cts +0 -17
  156. package/dist/types/extension.d.cts.map +0 -1
  157. package/dist/types/extension.d.mts +0 -17
  158. package/dist/types/extension.d.mts.map +0 -1
  159. package/dist/types/webWorktrees.cjs +0 -2
  160. package/dist/types/webWorktrees.cjs.map +0 -1
  161. package/dist/types/webWorktrees.mjs +0 -2
  162. package/dist/types/webWorktrees.mjs.map +0 -1
  163. package/dist/types/worktreeRegistry.d.cts +0 -41
  164. package/dist/types/worktreeRegistry.d.cts.map +0 -1
  165. package/dist/types/worktreeRegistry.d.mts +0 -41
  166. package/dist/types/worktreeRegistry.d.mts.map +0 -1
  167. package/dist/webHub.cjs +0 -1
  168. package/dist/webHub.d.cts +0 -2
  169. package/dist/webHub.d.mts +0 -2
  170. package/dist/webHub.mjs +0 -1
  171. package/src/exports/webClient.ts +0 -1
  172. package/src/types/webWorktrees.ts +0 -55
  173. package/src/types/worktreeRegistry.ts +0 -69
  174. package/src/web/components/RunWorktreeToolMessage.tsx +0 -37
  175. package/src/web/components/WorktreesActivitySection.tsx +0 -64
  176. package/src/web/components/WorktreesPanel.tsx +0 -125
  177. package/src/web/components/toolRenderers.ts +0 -7
  178. package/src/web/index.ts +0 -30
  179. package/src/web/stores/worktreesActivityStore.ts +0 -124
  180. package/src/web/tsconfig.json +0 -22
@@ -0,0 +1,911 @@
1
+ import { defineActivityGroup, defineChannelFile, defineFill, defineGlobalStore, defineSessionStore, defineToolRenderer, defineWebLifecycle, defineWebPlugin, workspaceApiPath } from "@agimon-ai/doompi-core/web";
2
+ import { Button, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Dot, DropdownMenuItem, EmptyState, Input, MessageItem, MessageItemBody, MessageItemHeader, OptionRow, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Spinner, StatusBadge, toolTone } from "@agimon-ai/doompi-web-components";
3
+ import { createElement, useEffect, useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { useStore } from "@tanstack/react-store";
6
+ //#region src/constants/git.ts
7
+ /** Workspace-mounted routes behind the new-session dialog. */
8
+ const GIT_BRANCHES_PATH = "/branches";
9
+ const GIT_SESSIONS_PATH = "/sessions";
10
+ //#endregion
11
+ //#region src/extensions/workspaces/(frontend)/_lib/newSessionForm.ts
12
+ const MAX_LISTED = 200;
13
+ /** Local branches first, then remote-only ones, filtered by a case-insensitive substring. */
14
+ function branchOptions(branches, query) {
15
+ const needle = query.trim().toLowerCase();
16
+ const matches = (text) => needle === "" || text.toLowerCase().includes(needle);
17
+ const local = branches.local.filter((branch) => matches(branch.name)).map((branch) => ({
18
+ key: `local:${branch.name}`,
19
+ name: branch.name,
20
+ current: branch.name === branches.current,
21
+ ...branch.checkedOutAt === void 0 ? {} : { busyAt: branch.checkedOutAt }
22
+ }));
23
+ const remote = branches.remote.filter((branch) => matches(`${branch.remote}/${branch.name}`)).map((branch) => ({
24
+ key: `remote:${branch.remote}/${branch.name}`,
25
+ name: branch.name,
26
+ remote: branch.remote,
27
+ current: false
28
+ }));
29
+ return [...local, ...remote].slice(0, MAX_LISTED);
30
+ }
31
+ /** Every base a new branch can start from: the default first, then remote-tracking names, then local branches. */
32
+ function baseOptions(branches) {
33
+ return [.../* @__PURE__ */ new Set([
34
+ ...branches.defaultBase === void 0 ? [] : [branches.defaultBase],
35
+ ...branches.remote.map((branch) => `${branch.remote}/${branch.name}`),
36
+ ...branches.local.map((branch) => branch.name)
37
+ ])];
38
+ }
39
+ /** Why the form cannot be submitted yet, or undefined when it can. */
40
+ function formProblem(state) {
41
+ if (state.mode === "existing") {
42
+ if (state.selected === void 0) return "Pick a branch.";
43
+ if (state.selected.busyAt !== void 0) return `That branch is checked out at ${state.selected.busyAt}.`;
44
+ }
45
+ if (state.mode === "new" && state.newBranch.trim() === "") return "Name the new branch.";
46
+ }
47
+ /** The worktree create request for the form, or undefined in plain mode. */
48
+ function createRequest(state) {
49
+ const name = state.name.trim();
50
+ const named = name === "" ? {} : { name };
51
+ if (state.mode === "existing" && state.selected !== void 0) return {
52
+ mode: "existing-branch",
53
+ branch: state.selected.name,
54
+ ...state.selected.remote === void 0 ? {} : { remote: state.selected.remote },
55
+ ...named
56
+ };
57
+ if (state.mode === "new") {
58
+ const baseRef = state.baseRef.trim();
59
+ return {
60
+ mode: "new-branch",
61
+ branch: state.newBranch.trim(),
62
+ ...baseRef === "" ? {} : { baseRef },
63
+ ...named
64
+ };
65
+ }
66
+ }
67
+ //#endregion
68
+ //#region src/extensions/workspaces/(frontend)/fill/_components/GitNewSessionDialog.tsx
69
+ const MODES = [
70
+ {
71
+ mode: "existing",
72
+ label: "existing branch"
73
+ },
74
+ {
75
+ mode: "new",
76
+ label: "new branch"
77
+ },
78
+ {
79
+ mode: "plain",
80
+ label: "no branch"
81
+ }
82
+ ];
83
+ function errorOf(body, status) {
84
+ return typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : `The cockpit answered ${String(status)}.`;
85
+ }
86
+ /**
87
+ * The new-session dialog when git is installed: a worktree on an existing or a
88
+ * new branch, or a plain session in the workspace root. A worktree session
89
+ * starts at the top level of the rail, with no parent.
90
+ */
91
+ function GitNewSessionDialog({ newSession }) {
92
+ const [branches, setBranches] = useState(null);
93
+ const [loadError, setLoadError] = useState("");
94
+ const [form, setForm] = useState({
95
+ mode: "existing",
96
+ newBranch: "",
97
+ baseRef: "",
98
+ name: ""
99
+ });
100
+ const [query, setQuery] = useState("");
101
+ const [error, setError] = useState("");
102
+ const [busy, setBusy] = useState(false);
103
+ const workspaceId = newSession?.workspaceId;
104
+ const request = newSession?.requestWithStepUp;
105
+ useEffect(() => {
106
+ if (workspaceId === void 0 || request === void 0) return;
107
+ let stale = false;
108
+ const url = `${workspaceApiPath(workspaceId)}/plugins/git${GIT_BRANCHES_PATH}`;
109
+ request(url).then(async (response) => {
110
+ const body = await response.json().catch(() => void 0);
111
+ if (stale) return;
112
+ if (!response.ok) {
113
+ setLoadError(errorOf(body, response.status));
114
+ return;
115
+ }
116
+ const loaded = body;
117
+ setBranches(loaded);
118
+ setForm((current) => ({
119
+ ...current,
120
+ baseRef: loaded.defaultBase ?? ""
121
+ }));
122
+ }).catch(() => {
123
+ if (!stale) setLoadError("The branches could not be loaded.");
124
+ });
125
+ return () => {
126
+ stale = true;
127
+ };
128
+ }, [workspaceId, request]);
129
+ if (newSession === void 0) return null;
130
+ const gitReady = branches?.repository === true && loadError === "";
131
+ const mode = gitReady || branches === null ? form.mode : "plain";
132
+ const state = {
133
+ ...form,
134
+ mode
135
+ };
136
+ const problem = formProblem(state);
137
+ const options = branches === null ? [] : branchOptions(branches, query);
138
+ const bases = branches === null ? [] : baseOptions(branches);
139
+ const update = (patch) => setForm((current) => ({
140
+ ...current,
141
+ ...patch
142
+ }));
143
+ const submit = async () => {
144
+ if (busy || problem !== void 0) return;
145
+ setBusy(true);
146
+ setError("");
147
+ const body = createRequest(state);
148
+ let failure;
149
+ if (body === void 0) failure = await newSession.createPlain(state.name);
150
+ else try {
151
+ const response = await newSession.requestWithStepUp(`${workspaceApiPath(newSession.workspaceId)}/plugins/git${GIT_SESSIONS_PATH}`, {
152
+ method: "POST",
153
+ headers: { "Content-Type": "application/json" },
154
+ body: JSON.stringify(body)
155
+ });
156
+ const answer = await response.json().catch(() => void 0);
157
+ failure = response.ok ? await newSession.openCreated(answer.sessionId) : errorOf(answer, response.status);
158
+ } catch {
159
+ failure = "The cockpit is unreachable.";
160
+ }
161
+ if (failure === void 0) return;
162
+ setError(failure);
163
+ setBusy(false);
164
+ };
165
+ return /* @__PURE__ */ jsx(Dialog, {
166
+ open: true,
167
+ onOpenChange: (next) => !next && !busy && newSession.close(),
168
+ children: /* @__PURE__ */ jsxs(DialogContent, {
169
+ width: "md",
170
+ "data-testid": "new-session-dialog",
171
+ "aria-describedby": void 0,
172
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "new session" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [
173
+ /* @__PURE__ */ jsx("p", {
174
+ className: "truncate text-xs text-doom-faint",
175
+ title: newSession.workspaceRoot,
176
+ children: newSession.workspaceRoot
177
+ }),
178
+ gitReady ? /* @__PURE__ */ jsx("div", {
179
+ role: "radiogroup",
180
+ "aria-label": "session kind",
181
+ className: "flex gap-1",
182
+ children: MODES.map((entry) => /* @__PURE__ */ jsx(Button, {
183
+ role: "radio",
184
+ "aria-checked": mode === entry.mode,
185
+ variant: mode === entry.mode ? "outline" : "ghost",
186
+ size: "sm",
187
+ "data-testid": `git-new-session-mode-${entry.mode}`,
188
+ disabled: busy,
189
+ className: mode === entry.mode ? "border-doom-blue/60 text-doom-hi" : void 0,
190
+ onClick: () => update({ mode: entry.mode }),
191
+ children: entry.label
192
+ }, entry.mode))
193
+ }) : branches === null && loadError === "" ? /* @__PURE__ */ jsx("p", {
194
+ className: "text-xs text-doom-faint",
195
+ children: "reading branches…"
196
+ }) : /* @__PURE__ */ jsx("p", {
197
+ "data-testid": "git-new-session-plain-note",
198
+ className: "text-xs text-doom-faint",
199
+ children: loadError || "This workspace is not a git checkout, so the session starts in its folder."
200
+ }),
201
+ mode === "existing" && gitReady ? /* @__PURE__ */ jsxs("div", {
202
+ className: "flex flex-col gap-1",
203
+ children: [/* @__PURE__ */ jsx(Input, {
204
+ "data-testid": "git-new-session-branch-search",
205
+ "aria-label": "find a branch",
206
+ value: query,
207
+ autoFocus: true,
208
+ disabled: busy,
209
+ onChange: (event) => setQuery(event.target.value),
210
+ placeholder: "find a branch"
211
+ }), /* @__PURE__ */ jsxs("div", {
212
+ role: "listbox",
213
+ "aria-label": "branches",
214
+ className: "max-h-48 overflow-y-auto rounded border border-doom-border bg-doom-deep p-1",
215
+ children: [options.length === 0 ? /* @__PURE__ */ jsx("p", {
216
+ className: "px-2 py-1 text-2xs text-doom-faint",
217
+ children: "no matching branches"
218
+ }) : null, options.map((option) => /* @__PURE__ */ jsxs(OptionRow, {
219
+ density: "compact",
220
+ active: form.selected?.key === option.key,
221
+ disabled: option.busyAt !== void 0 || busy,
222
+ "data-testid": "git-new-session-branch-option",
223
+ "data-branch": option.name,
224
+ "data-remote": option.remote,
225
+ onClick: () => update({ selected: option }),
226
+ className: `w-full px-2.5 py-1 text-sm ${option.busyAt !== void 0 ? "opacity-50" : ""}`,
227
+ children: [/* @__PURE__ */ jsx("span", {
228
+ className: "min-w-0 flex-1 truncate",
229
+ children: option.remote === void 0 ? option.name : `${option.remote}/${option.name}`
230
+ }), /* @__PURE__ */ jsx("span", {
231
+ className: "shrink-0 text-2xs text-doom-faint",
232
+ children: option.current && option.busyAt !== void 0 ? "current · checked out" : option.current ? "current" : option.busyAt !== void 0 ? "checked out" : option.remote !== void 0 ? "remote" : ""
233
+ })]
234
+ }, option.key))]
235
+ })]
236
+ }) : null,
237
+ mode === "new" && gitReady ? /* @__PURE__ */ jsxs("div", {
238
+ className: "flex flex-col gap-2",
239
+ children: [/* @__PURE__ */ jsxs("label", {
240
+ htmlFor: "git-new-session-new-branch",
241
+ className: "flex flex-col gap-1",
242
+ children: [/* @__PURE__ */ jsx("span", {
243
+ className: "text-xs text-doom-faint",
244
+ children: "new branch"
245
+ }), /* @__PURE__ */ jsx(Input, {
246
+ id: "git-new-session-new-branch",
247
+ "data-testid": "git-new-session-new-branch",
248
+ value: form.newBranch,
249
+ autoFocus: true,
250
+ disabled: busy,
251
+ onChange: (event) => update({ newBranch: event.target.value }),
252
+ placeholder: "feature/my-change"
253
+ })]
254
+ }), /* @__PURE__ */ jsxs("span", {
255
+ className: "flex flex-col gap-1",
256
+ children: [/* @__PURE__ */ jsx("span", {
257
+ className: "text-xs text-doom-faint",
258
+ children: "from"
259
+ }), /* @__PURE__ */ jsxs(Select, {
260
+ value: form.baseRef,
261
+ onValueChange: (baseRef) => update({ baseRef }),
262
+ disabled: busy,
263
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
264
+ "data-testid": "git-new-session-base",
265
+ "aria-label": "base branch",
266
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "base branch" })
267
+ }), /* @__PURE__ */ jsx(SelectContent, { children: bases.map((base) => /* @__PURE__ */ jsx(SelectItem, {
268
+ value: base,
269
+ children: base === branches?.current ? `${base} (current)` : base
270
+ }, base)) })]
271
+ })]
272
+ })]
273
+ }) : null,
274
+ /* @__PURE__ */ jsxs("label", {
275
+ htmlFor: "new-session-name",
276
+ className: "flex flex-col gap-1",
277
+ children: [/* @__PURE__ */ jsxs("span", {
278
+ className: "text-xs text-doom-faint",
279
+ children: [
280
+ "name",
281
+ " ",
282
+ /* @__PURE__ */ jsxs("span", {
283
+ className: "text-doom-faint",
284
+ children: [
285
+ "(optional",
286
+ mode === "plain" ? "" : "; defaults to the branch",
287
+ ")"
288
+ ]
289
+ })
290
+ ]
291
+ }), /* @__PURE__ */ jsx(Input, {
292
+ id: "new-session-name",
293
+ "data-testid": "new-session-name",
294
+ value: form.name,
295
+ autoFocus: mode === "plain",
296
+ disabled: busy,
297
+ onChange: (event) => update({ name: event.target.value }),
298
+ onKeyDown: (event) => {
299
+ if (event.key === "Enter") submit();
300
+ },
301
+ placeholder: "session name"
302
+ })]
303
+ }),
304
+ error ? /* @__PURE__ */ jsx("pre", {
305
+ "data-testid": "new-session-error",
306
+ className: "max-h-28 overflow-y-auto rounded border border-doom-edge-red bg-doom-tint-red/40 px-2.5 py-2 text-xs whitespace-pre-wrap text-doom-red",
307
+ children: error
308
+ }) : null,
309
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
310
+ variant: "outline",
311
+ "data-testid": "new-session-cancel",
312
+ disabled: busy,
313
+ onClick: () => newSession.close(),
314
+ children: "cancel"
315
+ }), /* @__PURE__ */ jsx(Button, {
316
+ variant: "primary",
317
+ "data-testid": "new-session-create",
318
+ title: problem,
319
+ disabled: busy || problem !== void 0,
320
+ onClick: () => void submit(),
321
+ children: busy ? mode === "plain" ? "creating…" : "creating worktree…" : "create"
322
+ })] })
323
+ ] })]
324
+ })
325
+ });
326
+ }
327
+ //#endregion
328
+ //#region src/extensions/workspaces/(frontend)/fill/GitNewSession.new-session.web.tsx
329
+ var GitNewSession_new_session_web_default = defineFill({ component: GitNewSessionDialog });
330
+ //#endregion
331
+ //#region src/types/webWorktrees.ts
332
+ /**
333
+ * The cockpit's view of this session's worktrees.
334
+ *
335
+ * DESIGN PATTERNS:
336
+ * - Declarations only. The channel frame type is a constant so the hub and the
337
+ * page cannot drift apart on a string.
338
+ * - A view is the subset of a record the dock needs, not the record. The
339
+ * registry may hold paths and provenance the page has no business rendering.
340
+ *
341
+ * AVOID:
342
+ * - Adding behaviour here. This file is imported by browser code, so it must
343
+ * stay free of node builtins.
344
+ */
345
+ /** The data channel carrying one session's worktrees to the page. */
346
+ const GIT_WORKTREES_TYPE = "git_worktrees";
347
+ //#endregion
348
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/worktreesActivityStore.ts
349
+ /**
350
+ * The dock's view of this session's worktrees, and the commands it sends.
351
+ *
352
+ * DESIGN PATTERNS:
353
+ * - Session-scoped store fed by one hub channel. The hub is authoritative; the
354
+ * page never reads a registry and never guesses.
355
+ * - Actions are channel frames, not prompts. The hub half owns the tool's
356
+ * operations, so the panel names the work and the hub performs it; a
357
+ * worktree made from the dock is the same object the tool makes, without
358
+ * spending a turn of the conversation to ask for it.
359
+ * - The sender lives in a store rather than a module variable, because a web
360
+ * plugin keeps no mutable module state and the page may reconnect.
361
+ *
362
+ * AVOID:
363
+ * - Calling the hub package API from here. A dock slot gets no sealed request
364
+ * transport, and raw fetch breaks the remote cockpit.
365
+ * - `sendSessionFrame` for a command. It envelopes the frame as a session
366
+ * command bound for the agent, which never reaches a channel's `receive`.
367
+ */
368
+ const worktreeActivity = defineSessionStore({
369
+ worktrees: [],
370
+ pending: void 0,
371
+ error: void 0,
372
+ errorTarget: void 0
373
+ });
374
+ /** Captures the clicked card, so a focus change cannot retarget an open dialog. */
375
+ const worktreeCreateDialog = defineGlobalStore(null);
376
+ /**
377
+ * The page's hub socket sender, held for as long as the plugin runs.
378
+ *
379
+ * Undefined before `startWorktreeRuntime` and after it disposes; a command
380
+ * sent in that window is dropped rather than queued, because a queue would
381
+ * replay a worktree create against a socket that has since changed sessions.
382
+ */
383
+ const worktreeSender = defineGlobalStore(void 0);
384
+ /** Binds the page's hub socket. The plugin host calls this once per page. */
385
+ function startWorktreeRuntime(runtime) {
386
+ const send = (frame) => {
387
+ runtime.sendHubFrame(frame);
388
+ };
389
+ worktreeSender.update(() => send);
390
+ return () => {
391
+ worktreeSender.update((current) => current === send ? void 0 : current);
392
+ worktreeCreateDialog.update(() => null);
393
+ };
394
+ }
395
+ /**
396
+ * Whether this session has worktree work in flight.
397
+ *
398
+ * A standing worktree is state, not work: it sits there until someone closes
399
+ * it, so counting it would leave the session reading as busy forever, with a
400
+ * "background work is still running" notice under a conversation where
401
+ * nothing is running. Only a create or close actually in progress counts.
402
+ *
403
+ * The group stays visible either way. Declaring an active source at all is
404
+ * what keeps it in the dock; this answers how it is drawn, not whether it is
405
+ * there.
406
+ */
407
+ const worktreeActivitySource = {
408
+ subscribe(listener) {
409
+ const subscription = worktreeActivity.store.subscribe(listener);
410
+ return () => subscription.unsubscribe();
411
+ },
412
+ isActive(sessionId) {
413
+ return worktreeActivity.select(worktreeActivity.store.state, sessionId).pending !== void 0;
414
+ }
415
+ };
416
+ function isRecord(value) {
417
+ return typeof value === "object" && value !== null && !Array.isArray(value);
418
+ }
419
+ function optionalText(value) {
420
+ return typeof value === "string" && value !== "" ? value : void 0;
421
+ }
422
+ function optionalErrorTarget(value) {
423
+ if (!isRecord(value)) return void 0;
424
+ if (value.action === "create") return { action: "create" };
425
+ if (value.action === "close" && typeof value.id === "string" && value.id !== "") return {
426
+ action: "close",
427
+ id: value.id
428
+ };
429
+ }
430
+ /** The plugin's session data channel: 'git_worktrees' payloads into the store. */
431
+ const worktreesChannel = worktreeActivity.channel({
432
+ channel: GIT_WORKTREES_TYPE,
433
+ parse(input) {
434
+ if (!isRecord(input) || !Array.isArray(input.worktrees)) return null;
435
+ return {
436
+ worktrees: input.worktrees.filter(isRecord),
437
+ pending: optionalText(input.pending),
438
+ error: optionalText(input.error),
439
+ errorTarget: optionalErrorTarget(input.errorTarget)
440
+ };
441
+ },
442
+ reduce(_current, next) {
443
+ return next;
444
+ }
445
+ });
446
+ /** Sends one command to the session's worktrees channel. */
447
+ function command(sessionId, payload) {
448
+ worktreeSender.store.state?.({
449
+ type: GIT_WORKTREES_TYPE,
450
+ sessionId,
451
+ payload
452
+ });
453
+ }
454
+ /** Asks the hub for a worktree. The hub answers on the same channel. */
455
+ function requestWorktreeCreate(sessionId, branch, baseRef, reservationId) {
456
+ const trimmedBase = baseRef.trim();
457
+ command(sessionId, {
458
+ action: "create",
459
+ branch: branch.trim(),
460
+ ...trimmedBase === "" ? {} : { baseRef: trimmedBase },
461
+ ...reservationId === void 0 ? {} : { reservationId }
462
+ });
463
+ }
464
+ /** Asks the hub to close one worktree. Refusal on a dirty tree stays the hub's call. */
465
+ function requestWorktreeClose(sessionId, id) {
466
+ command(sessionId, {
467
+ action: "close",
468
+ id
469
+ });
470
+ }
471
+ //#endregion
472
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/WorktreesPanel.tsx
473
+ /**
474
+ * The '# git' group's tab: this session's worktrees, and the form that makes one.
475
+ *
476
+ * DESIGN PATTERNS:
477
+ * - Lives in the activity dock, opened by the group's name. A worktree is work
478
+ * in progress belonging to the session that started it, so it sits beside
479
+ * agents, runners and workflows rather than in a settings screen.
480
+ * - Header, form, then cards, the same rhythm the runners and workflows panels
481
+ * use. A worktree row is a card because it carries three facts and two
482
+ * actions, which a single line cannot hold without becoming a puzzle.
483
+ * - Local form state is never reset by a data refresh. Direct lifecycle events
484
+ * update the list without discarding a half-typed branch name.
485
+ * - Acts through the plugin's own hub channel, so a worktree made here and one
486
+ * made by the tool take exactly the same path.
487
+ * - Creating waits on the cockpit starting a session, so the hub reports each
488
+ * phase on that channel and the header shows it turning. A button that looks
489
+ * idle for two minutes reads as broken.
490
+ *
491
+ * AVOID:
492
+ * - Deriving form state from refreshed store data instead of the user's input.
493
+ * - Colour classes outside the token set. `doom-error`, `doom-line` and
494
+ * `doom-bright` are not tokens: they compile to nothing and the row silently
495
+ * loses its border.
496
+ */
497
+ const WORKTREES_TAB_ID = "git-worktrees";
498
+ function worktreesTab() {
499
+ return {
500
+ id: WORKTREES_TAB_ID,
501
+ label: "worktrees",
502
+ panel: WorktreesPanel
503
+ };
504
+ }
505
+ function WorktreesPanel({ sessionId, sessionReservationId }) {
506
+ const session = useStore(worktreeActivity.store, (state) => worktreeActivity.select(state, sessionId));
507
+ const [branch, setBranch] = useState("");
508
+ const [baseRef, setBaseRef] = useState("");
509
+ const busy = session.pending !== void 0;
510
+ const canCreate = sessionId !== null && branch.trim() !== "" && !busy;
511
+ const createError = session.errorTarget?.action === "create" ? session.error : void 0;
512
+ const closeErrorId = session.errorTarget?.action === "close" ? session.errorTarget.id : void 0;
513
+ const panelError = session.error !== void 0 && (session.errorTarget === void 0 || closeErrorId !== void 0 && !session.worktrees.some((worktree) => worktree.id === closeErrorId));
514
+ const create = () => {
515
+ if (sessionId === null || !canCreate) return;
516
+ requestWorktreeCreate(sessionId, branch, baseRef, sessionReservationId);
517
+ if (sessionReservationId === void 0) {
518
+ setBranch("");
519
+ setBaseRef("");
520
+ }
521
+ };
522
+ return /* @__PURE__ */ jsxs("div", {
523
+ "data-testid": "git-worktrees-panel",
524
+ className: "flex min-h-0 flex-1 flex-col overflow-auto",
525
+ children: [
526
+ /* @__PURE__ */ jsxs("div", {
527
+ className: "flex min-h-11 shrink-0 items-center gap-2.5 border-b border-doom-border-soft px-3 sm:px-[26px]",
528
+ children: [
529
+ /* @__PURE__ */ jsx("span", {
530
+ className: "text-sm font-bold text-doom-hi",
531
+ children: "worktrees"
532
+ }),
533
+ /* @__PURE__ */ jsx("span", {
534
+ "data-testid": "git-worktrees-count",
535
+ className: "text-xs text-doom-faint",
536
+ children: session.worktrees.length === 0 ? "none yet" : `${String(session.worktrees.length)} open`
537
+ }),
538
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }),
539
+ session.pending === void 0 ? null : /* @__PURE__ */ jsxs("span", {
540
+ "data-testid": "git-worktree-pending",
541
+ className: "flex min-w-0 items-center gap-1.5",
542
+ children: [/* @__PURE__ */ jsx(Spinner, {
543
+ className: "h-3 w-3 shrink-0 text-doom-faint",
544
+ label: session.pending
545
+ }), /* @__PURE__ */ jsx("span", {
546
+ className: "min-w-0 truncate text-xs text-doom-dim",
547
+ children: session.pending
548
+ })]
549
+ })
550
+ ]
551
+ }),
552
+ /* @__PURE__ */ jsxs("div", {
553
+ className: "flex shrink-0 flex-col gap-1.5 border-b border-doom-border-soft px-3 py-2.5 sm:px-[26px]",
554
+ children: [/* @__PURE__ */ jsxs("div", {
555
+ className: "flex items-center gap-2",
556
+ children: [
557
+ /* @__PURE__ */ jsx(Input, {
558
+ "data-testid": "git-worktree-branch",
559
+ "aria-label": "Branch",
560
+ size: "sm",
561
+ className: "min-w-0 flex-1",
562
+ placeholder: "wt/fix-auth",
563
+ value: branch,
564
+ onChange: (event) => setBranch(event.target.value),
565
+ onKeyDown: (event) => {
566
+ if (event.key === "Enter") create();
567
+ }
568
+ }),
569
+ /* @__PURE__ */ jsx(Input, {
570
+ "data-testid": "git-worktree-base",
571
+ "aria-label": "Base ref",
572
+ size: "sm",
573
+ placeholder: "base (current)",
574
+ value: baseRef,
575
+ onChange: (event) => setBaseRef(event.target.value),
576
+ className: "w-32 shrink-0"
577
+ }),
578
+ /* @__PURE__ */ jsx(Button, {
579
+ "data-testid": "git-worktree-create",
580
+ size: "sm",
581
+ disabled: !canCreate,
582
+ onClick: create,
583
+ children: "Create"
584
+ })
585
+ ]
586
+ }), createError === void 0 ? /* @__PURE__ */ jsx("p", {
587
+ className: "text-2xs text-doom-faint",
588
+ children: "Creates the worktree outside the repository and starts a session nested under this one."
589
+ }) : /* @__PURE__ */ jsx("p", {
590
+ role: "alert",
591
+ "data-testid": "git-worktree-error",
592
+ className: "text-2xs text-doom-red",
593
+ children: createError
594
+ })]
595
+ }),
596
+ panelError ? /* @__PURE__ */ jsx("p", {
597
+ role: "alert",
598
+ "data-testid": "git-worktree-error",
599
+ className: "px-3 pt-2 text-2xs text-doom-red sm:px-[26px]",
600
+ children: session.error
601
+ }) : null,
602
+ session.worktrees.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
603
+ "data-testid": "git-worktrees-empty",
604
+ className: "px-4 py-8",
605
+ title: "no worktrees yet",
606
+ description: "a worktree is its own checkout on its own branch, with its own session nested under this one."
607
+ }) : /* @__PURE__ */ jsx("ul", {
608
+ "data-testid": "git-worktrees-list",
609
+ className: "flex flex-col gap-2.5 px-3 py-3 sm:px-[26px]",
610
+ children: session.worktrees.map((worktree) => /* @__PURE__ */ jsx(WorktreeCard, {
611
+ worktree,
612
+ sessionId,
613
+ busy,
614
+ error: closeErrorId === worktree.id ? session.error : void 0
615
+ }, worktree.id))
616
+ })
617
+ ]
618
+ });
619
+ }
620
+ /** One worktree: what it is, where it is, and the one thing you can do to it. */
621
+ function WorktreeCard({ worktree, sessionId, busy, error }) {
622
+ return /* @__PURE__ */ jsxs("li", {
623
+ "data-testid": `git-worktree-${worktree.id}`,
624
+ className: "flex min-w-0 flex-col gap-1.5 rounded-md border border-doom-border-soft bg-doom-panel p-2.5",
625
+ children: [
626
+ /* @__PURE__ */ jsxs("div", {
627
+ className: "flex min-w-0 items-center gap-1.5",
628
+ children: [
629
+ /* @__PURE__ */ jsx(Dot, { tone: worktree.orphaned ? "red" : worktree.sessionId === null ? "muted" : "blue" }),
630
+ /* @__PURE__ */ jsx("span", {
631
+ className: "min-w-0 flex-1 truncate text-sm font-bold text-doom-hi",
632
+ children: worktree.branch
633
+ }),
634
+ worktree.orphaned ? /* @__PURE__ */ jsx(StatusBadge, {
635
+ tone: "error",
636
+ size: "xs",
637
+ children: "orphaned"
638
+ }) : null,
639
+ worktree.unowned ? /* @__PURE__ */ jsx(StatusBadge, {
640
+ tone: "info",
641
+ size: "xs",
642
+ children: "unowned"
643
+ }) : null
644
+ ]
645
+ }),
646
+ /* @__PURE__ */ jsx("span", {
647
+ className: "min-w-0 truncate text-2xs text-doom-faint",
648
+ children: worktree.path
649
+ }),
650
+ /* @__PURE__ */ jsxs("div", {
651
+ className: "flex items-center gap-1.5 pt-0.5",
652
+ children: [/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }), sessionId === null ? null : /* @__PURE__ */ jsx(Button, {
653
+ variant: "danger-outline",
654
+ size: "xs",
655
+ disabled: busy,
656
+ "data-testid": `git-worktree-close-${worktree.id}`,
657
+ className: "px-2 text-2xs font-bold",
658
+ onClick: () => requestWorktreeClose(sessionId, worktree.id),
659
+ children: "close"
660
+ })]
661
+ }),
662
+ error === void 0 ? null : /* @__PURE__ */ jsx("p", {
663
+ role: "alert",
664
+ "data-testid": "git-worktree-error",
665
+ className: "text-2xs text-doom-red",
666
+ children: error
667
+ })
668
+ ]
669
+ });
670
+ }
671
+ //#endregion
672
+ //#region src/extensions/workspaces/sessions/(frontend)/activity-group/git.web.ts
673
+ var git_web_default = defineActivityGroup({
674
+ keys: "g w",
675
+ activeSource: worktreeActivitySource,
676
+ marksBackgroundWork: false,
677
+ transientTab: worktreesTab,
678
+ order: 30
679
+ });
680
+ //#endregion
681
+ //#region src/extensions/workspaces/sessions/(frontend)/channel/gitWorktrees.web.ts
682
+ var gitWorktrees_web_default = defineChannelFile(worktreesChannel);
683
+ //#endregion
684
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/_components/WorktreeCreateDialog.tsx
685
+ function WorktreeSessionMenu({ sessionId, sessionReservationId }) {
686
+ return /* @__PURE__ */ jsx(DropdownMenuItem, {
687
+ disabled: sessionId === null,
688
+ "data-testid": `session-worktree-${sessionReservationId ?? sessionId}`,
689
+ onSelect: () => {
690
+ if (sessionId !== null) worktreeCreateDialog.update(() => ({
691
+ sessionId,
692
+ ...sessionReservationId === void 0 ? {} : { reservationId: sessionReservationId }
693
+ }));
694
+ },
695
+ children: "create worktree session…"
696
+ });
697
+ }
698
+ function WorktreeCreateOverlay(props) {
699
+ const target = useStore(worktreeCreateDialog.store);
700
+ const { holdSessionChannels } = props;
701
+ useEffect(() => {
702
+ if (target === null) return;
703
+ return holdSessionChannels?.(target.sessionId, (frame) => {
704
+ if (frame.type !== worktreesChannel.channel) return;
705
+ const payload = worktreesChannel.parse(frame.payload);
706
+ if (payload !== null) worktreesChannel.apply(target.sessionId, payload);
707
+ });
708
+ }, [target, holdSessionChannels]);
709
+ if (target === null) return null;
710
+ return /* @__PURE__ */ jsx(Dialog, {
711
+ open: true,
712
+ onOpenChange: (open) => {
713
+ if (!open) worktreeCreateDialog.update(() => null);
714
+ },
715
+ children: /* @__PURE__ */ jsxs(DialogContent, {
716
+ width: "lg",
717
+ "data-testid": "session-worktree-dialog",
718
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "create worktree session" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [/* @__PURE__ */ jsx(DialogDescription, { children: "Creates a separate checkout and a new session. Existing sessions stay in their current directories." }), /* @__PURE__ */ createElement(WorktreesPanel, {
719
+ ...props,
720
+ key: `${target.sessionId}:${target.reservationId ?? ""}`,
721
+ sessionId: target.sessionId,
722
+ sessionReservationId: target.reservationId
723
+ })] })]
724
+ })
725
+ });
726
+ }
727
+ //#endregion
728
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/Worktree.session-menu.web.tsx
729
+ var Worktree_session_menu_web_default = defineFill({ component: WorktreeSessionMenu });
730
+ //#endregion
731
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/WorktreeCreate.overlay.web.tsx
732
+ var WorktreeCreate_overlay_web_default = defineFill({ component: WorktreeCreateOverlay });
733
+ //#endregion
734
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/_components/WorktreesActivitySection.tsx
735
+ /**
736
+ * The '# git' group's body in the activity dock.
737
+ *
738
+ * DESIGN PATTERNS:
739
+ * - One line per worktree, the same rule the runners rail follows: enough to
740
+ * notice it, not enough to work with it. The group's own name opens the tab
741
+ * that has room.
742
+ * - Idle still shows the launcher. A repository with no worktrees is the state
743
+ * where someone most wants to make one.
744
+ *
745
+ * AVOID:
746
+ * - Doing work here. This section reports and launches; the panel and the tool
747
+ * own everything else.
748
+ */
749
+ function WorktreesActivitySection({ sessionId, openTransientTab }) {
750
+ const session = useStore(worktreeActivity.store, (state) => worktreeActivity.select(state, sessionId));
751
+ if (session.worktrees.length === 0 && session.pending === void 0) return /* @__PURE__ */ jsxs("div", {
752
+ className: "flex items-center gap-2 px-1",
753
+ children: [/* @__PURE__ */ jsx("p", {
754
+ "data-testid": "activity-summary-git",
755
+ className: "px-1 text-xs text-doom-faint",
756
+ children: "idle"
757
+ }), sessionId === null ? null : /* @__PURE__ */ jsx(Button, {
758
+ variant: "link",
759
+ size: "xs",
760
+ "data-testid": "activity-git-create",
761
+ className: "px-0",
762
+ onClick: () => openTransientTab(worktreesTab()),
763
+ children: "create a worktree"
764
+ })]
765
+ });
766
+ return /* @__PURE__ */ jsxs("div", {
767
+ "data-testid": "activity-git-worktrees",
768
+ className: "flex flex-col gap-0.5",
769
+ children: [session.pending === void 0 ? null : /* @__PURE__ */ jsxs("span", {
770
+ className: "flex min-w-0 items-center gap-1.5 px-1",
771
+ children: [/* @__PURE__ */ jsx(Spinner, {
772
+ className: "h-3 w-3 shrink-0 text-doom-faint",
773
+ label: session.pending
774
+ }), /* @__PURE__ */ jsx("span", {
775
+ className: "min-w-0 truncate text-xs text-doom-dim",
776
+ children: session.pending
777
+ })]
778
+ }), session.worktrees.map((worktree) => /* @__PURE__ */ jsx(Button, {
779
+ variant: "ghost",
780
+ size: "card",
781
+ "data-testid": `activity-git-worktree-${worktree.id}`,
782
+ className: "min-w-0 gap-1.5 px-1",
783
+ onClick: () => openTransientTab(worktreesTab()),
784
+ children: /* @__PURE__ */ jsxs("span", {
785
+ className: "flex w-full min-w-0 items-center gap-1.5",
786
+ children: [
787
+ /* @__PURE__ */ jsx(Dot, { tone: worktree.orphaned ? "red" : worktree.sessionId === null ? "muted" : "blue" }),
788
+ /* @__PURE__ */ jsx("span", {
789
+ className: "min-w-0 flex-1 truncate text-left text-xs font-bold text-doom-hi",
790
+ children: worktree.branch
791
+ }),
792
+ worktree.orphaned ? /* @__PURE__ */ jsx(StatusBadge, {
793
+ tone: "error",
794
+ size: "xs",
795
+ children: "orphaned"
796
+ }) : null,
797
+ worktree.unowned ? /* @__PURE__ */ jsx(StatusBadge, {
798
+ tone: "info",
799
+ size: "xs",
800
+ children: "unowned"
801
+ }) : null
802
+ ]
803
+ })
804
+ }, worktree.id))]
805
+ });
806
+ }
807
+ //#endregion
808
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/Worktrees.activity.git.web.tsx
809
+ var Worktrees_activity_git_web_default = defineFill({ component: WorktreesActivitySection });
810
+ //#endregion
811
+ //#region src/extensions/workspaces/sessions/(frontend)/lifecycle/start.web.ts
812
+ var start_web_default = defineWebLifecycle(startWorktreeRuntime);
813
+ //#endregion
814
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_components/RunWorktreeToolMessage.tsx
815
+ const ACTION_LABEL = {
816
+ spawn_worktree: "Create worktree",
817
+ close_worktree: "Close worktree",
818
+ list: "List worktrees",
819
+ status: "Worktree status",
820
+ merge: "Merge worktree",
821
+ prune: "Prune worktrees",
822
+ send: "Message worktree",
823
+ messages: "Read worktree messages"
824
+ };
825
+ /**
826
+ * The timeline item for `run_worktree`.
827
+ *
828
+ * DESIGN PATTERNS:
829
+ * - Built on MessageItem, like every other tool renderer in the cockpit. That
830
+ * is what gives it the shared collapse toggle and the RUNNING/OK/ERROR badge
831
+ * rather than a hand-rolled ellipsis that never resolves.
832
+ * - The action and its subject go in the heading because that is what a reader
833
+ * scanning a transcript needs: "close_worktree a1b2c3d4" answers the question
834
+ * the raw JSON arguments make them decode.
835
+ * - A create takes as long as the cockpit needs to start a session, and the
836
+ * tool streams each phase into `output`, so the body is where the wait is
837
+ * visible.
838
+ */
839
+ function RunWorktreeToolMessage({ args, output, running, isError }) {
840
+ const action = typeof args.action === "string" ? args.action : "";
841
+ const subject = typeof args.branch === "string" ? args.branch : typeof args.id === "string" ? args.id : "";
842
+ return /* @__PURE__ */ jsxs(MessageItem, {
843
+ tone: toolTone({
844
+ running,
845
+ isError
846
+ }),
847
+ expandable: output !== "",
848
+ children: [/* @__PURE__ */ jsx(MessageItemHeader, {
849
+ title: ACTION_LABEL[action] ?? "Worktree",
850
+ children: subject === "" ? null : /* @__PURE__ */ jsx("span", {
851
+ className: "min-w-0 truncate text-doom-dim",
852
+ children: subject
853
+ })
854
+ }), output === "" ? null : /* @__PURE__ */ jsx(MessageItemBody, { children: /* @__PURE__ */ jsx("pre", {
855
+ className: `whitespace-pre-wrap text-xs ${isError ? "text-doom-red" : "text-doom-dim"}`,
856
+ children: output
857
+ }) })]
858
+ });
859
+ }
860
+ //#endregion
861
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/runWorktree.web.tsx
862
+ var runWorktree_web_default = defineToolRenderer({ message: RunWorktreeToolMessage });
863
+ //#endregion
864
+ //#region generated/web.ts
865
+ const via = (identity, value) => value === void 0 ? void 0 : {
866
+ ...identity,
867
+ ...value
868
+ };
869
+ const webPlugin = defineWebPlugin({
870
+ id: "git",
871
+ workspace: { get fills() {
872
+ return [via({
873
+ slot: "new-session",
874
+ id: "git-new-session"
875
+ }, GitNewSession_new_session_web_default)];
876
+ } },
877
+ session: {
878
+ get activityGroups() {
879
+ return [via({ name: "git" }, git_web_default)];
880
+ },
881
+ get channels() {
882
+ return [via({ channel: "git_worktrees" }, gitWorktrees_web_default)];
883
+ },
884
+ get fills() {
885
+ return [
886
+ via({
887
+ slot: "session-menu",
888
+ id: "worktree"
889
+ }, Worktree_session_menu_web_default),
890
+ via({
891
+ slot: "overlay",
892
+ id: "worktree-create"
893
+ }, WorktreeCreate_overlay_web_default),
894
+ via({
895
+ slot: "activity.git",
896
+ id: "worktrees"
897
+ }, Worktrees_activity_git_web_default)
898
+ ];
899
+ },
900
+ get start() {
901
+ return start_web_default;
902
+ },
903
+ get toolRenderers() {
904
+ return [via({ tools: ["run_worktree"] }, runWorktree_web_default)];
905
+ }
906
+ }
907
+ });
908
+ //#endregion
909
+ export { webPlugin };
910
+
911
+ //# sourceMappingURL=web.mjs.map