feat(app): delete workspace
This commit is contained in:
@@ -32,6 +32,7 @@ import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
|||||||
import { Collapsible } from "@opencode-ai/ui/collapsible"
|
import { Collapsible } from "@opencode-ai/ui/collapsible"
|
||||||
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
||||||
import { Spinner } from "@opencode-ai/ui/spinner"
|
import { Spinner } from "@opencode-ai/ui/spinner"
|
||||||
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
import { getFilename } from "@opencode-ai/util/path"
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
import { Session } from "@opencode-ai/sdk/v2/client"
|
import { Session } from "@opencode-ai/sdk/v2/client"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
@@ -906,6 +907,99 @@ export default function Layout(props: ParentProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errorMessage = (err: unknown) => {
|
||||||
|
if (err && typeof err === "object" && "data" in err) {
|
||||||
|
const data = (err as { data?: { message?: string } }).data
|
||||||
|
if (data?.message) return data.message
|
||||||
|
}
|
||||||
|
if (err instanceof Error) return err.message
|
||||||
|
return "Request failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteWorkspace = async (directory: string) => {
|
||||||
|
const current = currentProject()
|
||||||
|
if (!current) return
|
||||||
|
if (directory === current.worktree) return
|
||||||
|
|
||||||
|
const result = await globalSDK.client.worktree
|
||||||
|
.remove({ directory: current.worktree, worktreeRemoveInput: { directory } })
|
||||||
|
.then((x) => x.data)
|
||||||
|
.catch((err) => {
|
||||||
|
showToast({
|
||||||
|
title: "Failed to delete workspace",
|
||||||
|
description: errorMessage(err),
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!result) return
|
||||||
|
|
||||||
|
layout.projects.close(directory)
|
||||||
|
layout.projects.open(current.worktree)
|
||||||
|
|
||||||
|
if (params.dir && base64Decode(params.dir) === directory) {
|
||||||
|
navigateToProject(current.worktree)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogDeleteWorkspace(props: { directory: string }) {
|
||||||
|
const name = createMemo(() => getFilename(props.directory))
|
||||||
|
const [data, setData] = createStore({
|
||||||
|
status: "loading" as "loading" | "ready" | "error",
|
||||||
|
dirty: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const current = currentProject()
|
||||||
|
if (!current) {
|
||||||
|
setData({ status: "error", dirty: false })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
globalSDK.client.file
|
||||||
|
.status({ directory: props.directory })
|
||||||
|
.then((x) => {
|
||||||
|
const files = x.data ?? []
|
||||||
|
const dirty = files.length > 0
|
||||||
|
setData({ status: "ready", dirty })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setData({ status: "error", dirty: false })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
await deleteWorkspace(props.directory)
|
||||||
|
dialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
const description = () => {
|
||||||
|
if (data.status === "loading") return "Checking for unmerged changes..."
|
||||||
|
if (data.status === "error") return "Unable to verify git status."
|
||||||
|
if (!data.dirty) return "No unmerged changes detected."
|
||||||
|
return "Unmerged changes detected in this workspace."
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog title="Delete workspace">
|
||||||
|
<div class="flex flex-col gap-4 px-2.5 pb-3">
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<span class="text-14-regular text-text-strong">Delete workspace "{name()}"?</span>
|
||||||
|
<span class="text-12-regular text-text-weak">{description()}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<Button variant="ghost" size="large" onClick={() => dialog.close()}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="primary" size="large" disabled={data.status === "loading"} onClick={handleDelete}>
|
||||||
|
Delete workspace
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
on(
|
on(
|
||||||
() => ({ ready: pageReady(), dir: params.dir, id: params.id }),
|
() => ({ ready: pageReady(), dir: params.dir, id: params.id }),
|
||||||
@@ -1205,6 +1299,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const SortableWorkspace = (props: { directory: string; project: LocalProject; mobile?: boolean }): JSX.Element => {
|
const SortableWorkspace = (props: { directory: string; project: LocalProject; mobile?: boolean }): JSX.Element => {
|
||||||
const sortable = createSortable(props.directory)
|
const sortable = createSortable(props.directory)
|
||||||
const [workspaceStore, setWorkspaceStore] = globalSync.child(props.directory)
|
const [workspaceStore, setWorkspaceStore] = globalSync.child(props.directory)
|
||||||
|
const [menuOpen, setMenuOpen] = createSignal(false)
|
||||||
const slug = createMemo(() => base64Encode(props.directory))
|
const slug = createMemo(() => base64Encode(props.directory))
|
||||||
const sessions = createMemo(() =>
|
const sessions = createMemo(() =>
|
||||||
workspaceStore.session
|
workspaceStore.session
|
||||||
@@ -1239,62 +1334,85 @@ export default function Layout(props: ParentProps) {
|
|||||||
<div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
|
<div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
|
||||||
<Collapsible variant="ghost" open={open()} class="shrink-0" onOpenChange={openWrapper}>
|
<Collapsible variant="ghost" open={open()} class="shrink-0" onOpenChange={openWrapper}>
|
||||||
<div class="px-2 py-1">
|
<div class="px-2 py-1">
|
||||||
<div class="group/trigger relative">
|
<div class="group/workspace relative">
|
||||||
<Collapsible.Trigger class="flex items-center justify-between w-full pl-2 pr-16 py-1.5 rounded-md hover:bg-surface-raised-base-hover">
|
<div class="flex items-center gap-1">
|
||||||
<div class="flex items-center gap-1 min-w-0 flex-1">
|
<Collapsible.Trigger class="flex items-center justify-between w-full pl-2 pr-16 py-1.5 rounded-md hover:bg-surface-raised-base-hover">
|
||||||
<div class="flex items-center justify-center shrink-0 size-6">
|
<div class="flex items-center gap-1 min-w-0 flex-1">
|
||||||
<Icon name="branch" size="small" />
|
<div class="flex items-center justify-center shrink-0 size-6">
|
||||||
</div>
|
<Icon name="branch" size="small" />
|
||||||
<span class="text-14-medium text-text-base shrink-0">{local() ? "local" : "sandbox"} :</span>
|
</div>
|
||||||
<Show
|
<span class="text-14-medium text-text-base shrink-0">{local() ? "local" : "sandbox"} :</span>
|
||||||
when={!local()}
|
<Show
|
||||||
fallback={
|
when={!local()}
|
||||||
<span class="text-14-medium text-text-base min-w-0 truncate">
|
fallback={
|
||||||
{workspaceStore.vcs?.branch ?? getFilename(props.directory)}
|
<span class="text-14-medium text-text-base min-w-0 truncate">
|
||||||
</span>
|
{workspaceStore.vcs?.branch ?? getFilename(props.directory)}
|
||||||
}
|
</span>
|
||||||
>
|
}
|
||||||
<InlineEditor
|
>
|
||||||
id={`workspace:${props.directory}`}
|
<InlineEditor
|
||||||
value={workspaceValue}
|
id={`workspace:${props.directory}`}
|
||||||
onSave={(next) => {
|
value={workspaceValue}
|
||||||
const trimmed = next.trim()
|
onSave={(next) => {
|
||||||
if (!trimmed) return
|
const trimmed = next.trim()
|
||||||
renameWorkspace(props.directory, trimmed)
|
if (!trimmed) return
|
||||||
setEditor("value", workspaceValue())
|
renameWorkspace(props.directory, trimmed)
|
||||||
}}
|
setEditor("value", workspaceValue())
|
||||||
class="text-14-medium text-text-base min-w-0 truncate"
|
}}
|
||||||
displayClass="text-14-medium text-text-base min-w-0 truncate"
|
class="text-14-medium text-text-base min-w-0 truncate"
|
||||||
editing={workspaceEditActive()}
|
displayClass="text-14-medium text-text-base min-w-0 truncate"
|
||||||
stopPropagation={false}
|
editing={workspaceEditActive()}
|
||||||
openOnDblClick={false}
|
stopPropagation={false}
|
||||||
|
openOnDblClick={false}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
<Icon
|
||||||
|
name={open() ? "chevron-down" : "chevron-right"}
|
||||||
|
size="small"
|
||||||
|
class="shrink-0 text-icon-base opacity-0 transition-opacity group-hover/workspace:opacity-100 group-focus-within/workspace:opacity-100"
|
||||||
/>
|
/>
|
||||||
</Show>
|
</div>
|
||||||
<Icon
|
</Collapsible.Trigger>
|
||||||
name={open() ? "chevron-down" : "chevron-right"}
|
<div
|
||||||
size="small"
|
class="absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-0.5 transition-opacity"
|
||||||
class="shrink-0 text-icon-base opacity-0 transition-opacity group-hover/trigger:opacity-100 group-focus-within/trigger:opacity-100"
|
classList={{
|
||||||
/>
|
"opacity-100 pointer-events-auto": menuOpen(),
|
||||||
</div>
|
"opacity-0 pointer-events-none": !menuOpen(),
|
||||||
</Collapsible.Trigger>
|
"group-hover/workspace:opacity-100 group-hover/workspace:pointer-events-auto": true,
|
||||||
<div class="absolute right-1 top-1/2 -translate-y-1/2 hidden items-center gap-0.5 pointer-events-none group-hover/trigger:flex group-focus-within/trigger:flex">
|
"group-focus-within/workspace:opacity-100 group-focus-within/workspace:pointer-events-auto": true,
|
||||||
<IconButton icon="dot-grid" variant="ghost" class="size-6 rounded-md pointer-events-auto" />
|
}}
|
||||||
<TooltipKeybind
|
|
||||||
class="pointer-events-auto"
|
|
||||||
placement="right"
|
|
||||||
title="New session"
|
|
||||||
keybind={command.keybind("session.new")}
|
|
||||||
>
|
>
|
||||||
<IconButton
|
<DropdownMenu open={menuOpen()} onOpenChange={setMenuOpen}>
|
||||||
icon="plus-small"
|
<Tooltip value="More options" placement="top">
|
||||||
variant="ghost"
|
<DropdownMenu.Trigger as={IconButton} icon="dot-grid" variant="ghost" class="size-6 rounded-md" />
|
||||||
class="size-6 rounded-md"
|
</Tooltip>
|
||||||
onClick={() => navigate(`/${slug()}/session`)}
|
<DropdownMenu.Portal>
|
||||||
/>
|
<DropdownMenu.Content>
|
||||||
</TooltipKeybind>
|
<DropdownMenu.Item onSelect={() => navigate(`/${slug()}/session`)}>
|
||||||
|
<DropdownMenu.ItemLabel>New session</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
disabled={local()}
|
||||||
|
onSelect={() => dialog.show(() => <DialogDeleteWorkspace directory={props.directory} />)}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemLabel>Delete workspace</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu>
|
||||||
|
<TooltipKeybind placement="right" title="New session" keybind={command.keybind("session.new")}>
|
||||||
|
<IconButton
|
||||||
|
icon="plus-small"
|
||||||
|
variant="ghost"
|
||||||
|
class="size-6 rounded-md"
|
||||||
|
onClick={() => navigate(`/${slug()}/session`)}
|
||||||
|
/>
|
||||||
|
</TooltipKeybind>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Collapsible.Content>
|
<Collapsible.Content>
|
||||||
<nav class="flex flex-col gap-1 px-2">
|
<nav class="flex flex-col gap-1 px-2">
|
||||||
<Button
|
<Button
|
||||||
@@ -1520,15 +1638,6 @@ export default function Layout(props: ParentProps) {
|
|||||||
const projectId = createMemo(() => project()?.id ?? "")
|
const projectId = createMemo(() => project()?.id ?? "")
|
||||||
const workspaces = createMemo(() => workspaceIds(project()))
|
const workspaces = createMemo(() => workspaceIds(project()))
|
||||||
|
|
||||||
const errorMessage = (err: unknown) => {
|
|
||||||
if (err && typeof err === "object" && "data" in err) {
|
|
||||||
const data = (err as { data?: { message?: string } }).data
|
|
||||||
if (data?.message) return data.message
|
|
||||||
}
|
|
||||||
if (err instanceof Error) return err.message
|
|
||||||
return "Request failed"
|
|
||||||
}
|
|
||||||
|
|
||||||
const createWorkspace = async () => {
|
const createWorkspace = async () => {
|
||||||
const current = project()
|
const current = project()
|
||||||
if (!current) return
|
if (!current) return
|
||||||
|
|||||||
@@ -317,4 +317,19 @@ export namespace Project {
|
|||||||
}
|
}
|
||||||
return valid
|
return valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeSandbox(projectID: string, directory: string) {
|
||||||
|
const result = await Storage.update<Info>(["project", projectID], (draft) => {
|
||||||
|
const sandboxes = draft.sandboxes ?? []
|
||||||
|
draft.sandboxes = sandboxes.filter((sandbox) => sandbox !== directory)
|
||||||
|
draft.time.updated = Date.now()
|
||||||
|
})
|
||||||
|
GlobalBus.emit("event", {
|
||||||
|
payload: {
|
||||||
|
type: Event.Updated.type,
|
||||||
|
properties: result,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,32 @@ export const ExperimentalRoutes = lazy(() =>
|
|||||||
return c.json(sandboxes)
|
return c.json(sandboxes)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.delete(
|
||||||
|
"/worktree",
|
||||||
|
describeRoute({
|
||||||
|
summary: "Remove worktree",
|
||||||
|
description: "Remove a git worktree and delete its branch.",
|
||||||
|
operationId: "worktree.remove",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Worktree removed",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(z.boolean()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...errors(400),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
validator("json", Worktree.remove.schema),
|
||||||
|
async (c) => {
|
||||||
|
const body = c.req.valid("json")
|
||||||
|
await Worktree.remove(body)
|
||||||
|
await Project.removeSandbox(Instance.project.id, body.directory)
|
||||||
|
return c.json(true)
|
||||||
|
},
|
||||||
|
)
|
||||||
.get(
|
.get(
|
||||||
"/resource",
|
"/resource",
|
||||||
describeRoute({
|
describeRoute({
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ export namespace Worktree {
|
|||||||
|
|
||||||
export type CreateInput = z.infer<typeof CreateInput>
|
export type CreateInput = z.infer<typeof CreateInput>
|
||||||
|
|
||||||
|
export const RemoveInput = z
|
||||||
|
.object({
|
||||||
|
directory: z.string(),
|
||||||
|
})
|
||||||
|
.meta({
|
||||||
|
ref: "WorktreeRemoveInput",
|
||||||
|
})
|
||||||
|
|
||||||
|
export type RemoveInput = z.infer<typeof RemoveInput>
|
||||||
|
|
||||||
export const NotGitError = NamedError.create(
|
export const NotGitError = NamedError.create(
|
||||||
"WorktreeNotGitError",
|
"WorktreeNotGitError",
|
||||||
z.object({
|
z.object({
|
||||||
@@ -61,6 +71,13 @@ export namespace Worktree {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const RemoveFailedError = NamedError.create(
|
||||||
|
"WorktreeRemoveFailedError",
|
||||||
|
z.object({
|
||||||
|
message: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const ADJECTIVES = [
|
const ADJECTIVES = [
|
||||||
"brave",
|
"brave",
|
||||||
"calm",
|
"calm",
|
||||||
@@ -214,4 +231,53 @@ export namespace Worktree {
|
|||||||
|
|
||||||
return info
|
return info
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const remove = fn(RemoveInput, async (input) => {
|
||||||
|
if (Instance.project.vcs !== "git") {
|
||||||
|
throw new NotGitError({ message: "Worktrees are only supported for git projects" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const directory = path.resolve(input.directory)
|
||||||
|
const list = await $`git worktree list --porcelain`.quiet().nothrow().cwd(Instance.worktree)
|
||||||
|
if (list.exitCode !== 0) {
|
||||||
|
throw new RemoveFailedError({ message: errorText(list) || "Failed to read git worktrees" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = outputText(list.stdout)
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => line.trim())
|
||||||
|
const entries = lines.reduce<{ path?: string; branch?: string }[]>((acc, line) => {
|
||||||
|
if (!line) return acc
|
||||||
|
if (line.startsWith("worktree ")) {
|
||||||
|
acc.push({ path: line.slice("worktree ".length).trim() })
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
const current = acc[acc.length - 1]
|
||||||
|
if (!current) return acc
|
||||||
|
if (line.startsWith("branch ")) {
|
||||||
|
current.branch = line.slice("branch ".length).trim()
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const entry = entries.find((item) => item.path && path.resolve(item.path) === directory)
|
||||||
|
if (!entry?.path) {
|
||||||
|
throw new RemoveFailedError({ message: "Worktree not found" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const removed = await $`git worktree remove --force ${entry.path}`.quiet().nothrow().cwd(Instance.worktree)
|
||||||
|
if (removed.exitCode !== 0) {
|
||||||
|
throw new RemoveFailedError({ message: errorText(removed) || "Failed to remove git worktree" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const branch = entry.branch?.replace(/^refs\/heads\//, "")
|
||||||
|
if (branch) {
|
||||||
|
const deleted = await $`git branch -D ${branch}`.quiet().nothrow().cwd(Instance.worktree)
|
||||||
|
if (deleted.exitCode !== 0) {
|
||||||
|
throw new RemoveFailedError({ message: errorText(deleted) || "Failed to delete worktree branch" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,9 @@ import type {
|
|||||||
WorktreeCreateInput,
|
WorktreeCreateInput,
|
||||||
WorktreeCreateResponses,
|
WorktreeCreateResponses,
|
||||||
WorktreeListResponses,
|
WorktreeListResponses,
|
||||||
|
WorktreeRemoveErrors,
|
||||||
|
WorktreeRemoveInput,
|
||||||
|
WorktreeRemoveResponses,
|
||||||
} from "./types.gen.js"
|
} from "./types.gen.js"
|
||||||
|
|
||||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<
|
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<
|
||||||
@@ -654,6 +657,41 @@ export class Tool extends HeyApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Worktree extends HeyApiClient {
|
export class Worktree extends HeyApiClient {
|
||||||
|
/**
|
||||||
|
* Remove worktree
|
||||||
|
*
|
||||||
|
* Remove a git worktree and delete its branch.
|
||||||
|
*/
|
||||||
|
public remove<ThrowOnError extends boolean = false>(
|
||||||
|
parameters?: {
|
||||||
|
directory?: string
|
||||||
|
worktreeRemoveInput?: WorktreeRemoveInput
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams(
|
||||||
|
[parameters],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
args: [
|
||||||
|
{ in: "query", key: "directory" },
|
||||||
|
{ key: "worktreeRemoveInput", map: "body" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return (options?.client ?? this.client).delete<WorktreeRemoveResponses, WorktreeRemoveErrors, ThrowOnError>({
|
||||||
|
url: "/experimental/worktree",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options?.headers,
|
||||||
|
...params.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List worktrees
|
* List worktrees
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1908,6 +1908,10 @@ export type WorktreeCreateInput = {
|
|||||||
startCommand?: string
|
startCommand?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WorktreeRemoveInput = {
|
||||||
|
directory: string
|
||||||
|
}
|
||||||
|
|
||||||
export type McpResource = {
|
export type McpResource = {
|
||||||
name: string
|
name: string
|
||||||
uri: string
|
uri: string
|
||||||
@@ -2554,6 +2558,33 @@ export type ToolListResponses = {
|
|||||||
|
|
||||||
export type ToolListResponse = ToolListResponses[keyof ToolListResponses]
|
export type ToolListResponse = ToolListResponses[keyof ToolListResponses]
|
||||||
|
|
||||||
|
export type WorktreeRemoveData = {
|
||||||
|
body?: WorktreeRemoveInput
|
||||||
|
path?: never
|
||||||
|
query?: {
|
||||||
|
directory?: string
|
||||||
|
}
|
||||||
|
url: "/experimental/worktree"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WorktreeRemoveErrors = {
|
||||||
|
/**
|
||||||
|
* Bad request
|
||||||
|
*/
|
||||||
|
400: BadRequestError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WorktreeRemoveError = WorktreeRemoveErrors[keyof WorktreeRemoveErrors]
|
||||||
|
|
||||||
|
export type WorktreeRemoveResponses = {
|
||||||
|
/**
|
||||||
|
* Worktree removed
|
||||||
|
*/
|
||||||
|
200: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WorktreeRemoveResponse = WorktreeRemoveResponses[keyof WorktreeRemoveResponses]
|
||||||
|
|
||||||
export type WorktreeListData = {
|
export type WorktreeListData = {
|
||||||
body?: never
|
body?: never
|
||||||
path?: never
|
path?: never
|
||||||
|
|||||||
Reference in New Issue
Block a user