1import * as Schema from "effect/Schema";
2
3import { TrimmedNonEmptyString } from "./baseSchemas.ts";
4
5/**
6 * Input for the `t3_worktree_handoff` MCP tool.
7 *
8 * Creates a git worktree for the calling agent thread and re-points the
9 * thread at it. Changing the thread's workspace detaches the live provider
10 * session, so the current turn ends shortly after the handoff is recorded;
11 * the conversation continues inside the worktree on the thread's next run.
12 */
13export const WorktreeMcpHandoffInput = Schema.Struct({
14 branch: TrimmedNonEmptyString.annotate({
15 description: "Branch name to create for the worktree (e.g. 'feature/my-change').",
16 }),
17 baseRef: Schema.optional(
18 TrimmedNonEmptyString.annotate({
19 description:
20 "Branch or ref the worktree branch starts from. Defaults to the branch currently checked out in the project workspace.",
21 }),
22 ),
23 startFromOrigin: Schema.optional(
24 Schema.Boolean.annotate({
25 description:
26 "Fetch origin and start the worktree branch from the remote-tracking commit of baseRef instead of the local ref. Defaults to the server's 'new worktrees start from origin' setting.",
27 }),
28 ),
29 path: Schema.optional(
30 TrimmedNonEmptyString.check(
31 // Absolute POSIX (/...), Windows drive (C:\ or C:/), or UNC (\\host).
32 Schema.isPattern(/^(?:[A-Za-z]:[\\/]|[\\/])/),
33 ).annotate({
34 description:
35 "Absolute filesystem path for the new worktree. Relative paths are rejected. Defaults to the server-managed worktrees directory.",
36 }),
37 ),
38 runSetupScript: Schema.optional(
39 Schema.Boolean.annotate({
40 description:
41 "Run the project's configured setup script in the new worktree after handoff. Defaults to true.",
42 }),
43 ),
44 continuationPrompt: Schema.optional(
45 TrimmedNonEmptyString.check(Schema.isMaxLength(120_000)).annotate({
46 description:
47 "Message queued as the thread's next turn after the handoff. The handoff detaches the current provider session, so pass the remaining work here to automatically resume inside the worktree; omit it to stop after the handoff and wait for the next message.",
48 }),
49 ),
50});
51export type WorktreeMcpHandoffInput = typeof WorktreeMcpHandoffInput.Type;
52
53export const WorktreeMcpSetupScriptStatus = Schema.Union([
54 Schema.Struct({
55 status: Schema.Literal("started"),
56 scriptName: TrimmedNonEmptyString,
57 terminalId: TrimmedNonEmptyString,
58 }),
59 Schema.Struct({
60 status: Schema.Literal("no-script"),
61 }),
62 Schema.Struct({
63 status: Schema.Literal("skipped"),
64 }),
65 Schema.Struct({
66 status: Schema.Literal("failed"),
67 detail: Schema.String,
68 }),
69]);
70export type WorktreeMcpSetupScriptStatus = typeof WorktreeMcpSetupScriptStatus.Type;
71
72export const WorktreeMcpContinuationStatus = Schema.Union([
73 Schema.Struct({
74 status: Schema.Literal("scheduled"),
75 delivery: Schema.Literals(["started", "queued", "steered", "restarted"]),
76 }),
77 Schema.Struct({
78 status: Schema.Literal("skipped"),
79 }),
80 Schema.Struct({
81 status: Schema.Literal("failed"),
82 detail: Schema.String,
83 }),
84]);
85export type WorktreeMcpContinuationStatus = typeof WorktreeMcpContinuationStatus.Type;
86
87export const WorktreeMcpHandoffResult = Schema.Struct({
88 worktreePath: TrimmedNonEmptyString,
89 branch: TrimmedNonEmptyString,
90 baseRef: TrimmedNonEmptyString,
91 startedFromOrigin: Schema.Boolean,
92 setupScript: WorktreeMcpSetupScriptStatus,
93 continuation: WorktreeMcpContinuationStatus,
94 note: Schema.String,
95});
96export type WorktreeMcpHandoffResult = typeof WorktreeMcpHandoffResult.Type;
97
98export const WorktreeMcpStatusResult = Schema.Struct({
99 attached: Schema.Boolean.annotate({
100 description: "True when this thread is already attached to a git worktree.",
101 }),
102 worktreePath: Schema.NullOr(TrimmedNonEmptyString),
103 branch: Schema.NullOr(TrimmedNonEmptyString),
104 projectWorkspaceRoot: TrimmedNonEmptyString.annotate({
105 description: "Root of the project's main workspace checkout.",
106 }),
107 defaultStartFromOrigin: Schema.Boolean.annotate({
108 description: "Server default used by t3_worktree_handoff when startFromOrigin is omitted.",
109 }),
110});
111export type WorktreeMcpStatusResult = typeof WorktreeMcpStatusResult.Type;
112
113export class WorktreeMcpFailure extends Schema.TaggedError<WorktreeMcpFailure>()(
114 "WorktreeMcpFailure",
115 {
116 code: Schema.Literals([
117 "capability_denied",
118 "thread_not_found",
119 "project_not_found",
120 "already_in_worktree",
121 "handoff_in_progress",
122 "invalid_request",
123 "operation_failed",
124 "thread_credential_required",
125 ]),
126 message: Schema.String,
127 },
128) {}