wave-agent-sdk 0.15.1 → 0.15.2

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 (111) hide show
  1. package/builtin/skills/loop/SKILL.md +29 -3
  2. package/dist/agent.d.ts +7 -2
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +34 -11
  5. package/dist/constants/tools.d.ts +3 -0
  6. package/dist/constants/tools.d.ts.map +1 -1
  7. package/dist/constants/tools.js +3 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/managers/aiManager.d.ts +13 -1
  12. package/dist/managers/aiManager.d.ts.map +1 -1
  13. package/dist/managers/aiManager.js +69 -17
  14. package/dist/managers/hookManager.d.ts.map +1 -1
  15. package/dist/managers/hookManager.js +9 -0
  16. package/dist/managers/mcpManager.d.ts +4 -1
  17. package/dist/managers/mcpManager.d.ts.map +1 -1
  18. package/dist/managers/mcpManager.js +25 -5
  19. package/dist/managers/permissionManager.d.ts +0 -2
  20. package/dist/managers/permissionManager.d.ts.map +1 -1
  21. package/dist/managers/permissionManager.js +0 -30
  22. package/dist/managers/slashCommandManager.d.ts +1 -0
  23. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  24. package/dist/managers/slashCommandManager.js +4 -0
  25. package/dist/managers/toolManager.d.ts +6 -0
  26. package/dist/managers/toolManager.d.ts.map +1 -1
  27. package/dist/managers/toolManager.js +41 -1
  28. package/dist/prompts/index.d.ts.map +1 -1
  29. package/dist/prompts/index.js +14 -4
  30. package/dist/services/initializationService.d.ts +0 -2
  31. package/dist/services/initializationService.d.ts.map +1 -1
  32. package/dist/services/initializationService.js +3 -35
  33. package/dist/services/memory.d.ts +6 -0
  34. package/dist/services/memory.d.ts.map +1 -1
  35. package/dist/services/memory.js +27 -14
  36. package/dist/tools/cronCreateTool.d.ts.map +1 -1
  37. package/dist/tools/cronCreateTool.js +71 -6
  38. package/dist/tools/cronDeleteTool.d.ts.map +1 -1
  39. package/dist/tools/cronDeleteTool.js +5 -1
  40. package/dist/tools/cronListTool.d.ts.map +1 -1
  41. package/dist/tools/cronListTool.js +5 -1
  42. package/dist/tools/enterWorktreeTool.d.ts +8 -0
  43. package/dist/tools/enterWorktreeTool.d.ts.map +1 -0
  44. package/dist/tools/enterWorktreeTool.js +144 -0
  45. package/dist/tools/exitWorktreeTool.d.ts +8 -0
  46. package/dist/tools/exitWorktreeTool.d.ts.map +1 -0
  47. package/dist/tools/exitWorktreeTool.js +184 -0
  48. package/dist/tools/taskManagementTools.d.ts.map +1 -1
  49. package/dist/tools/taskManagementTools.js +4 -0
  50. package/dist/tools/toolSearchTool.d.ts +15 -0
  51. package/dist/tools/toolSearchTool.d.ts.map +1 -0
  52. package/dist/tools/toolSearchTool.js +185 -0
  53. package/dist/tools/types.d.ts +19 -0
  54. package/dist/tools/types.d.ts.map +1 -1
  55. package/dist/tools/webFetchTool.d.ts.map +1 -1
  56. package/dist/tools/webFetchTool.js +1 -0
  57. package/dist/types/agent.d.ts +6 -1
  58. package/dist/types/agent.d.ts.map +1 -1
  59. package/dist/types/hooks.d.ts +3 -1
  60. package/dist/types/hooks.d.ts.map +1 -1
  61. package/dist/types/hooks.js +1 -0
  62. package/dist/utils/containerSetup.d.ts.map +1 -1
  63. package/dist/utils/containerSetup.js +4 -6
  64. package/dist/utils/cronToHuman.d.ts +6 -0
  65. package/dist/utils/cronToHuman.d.ts.map +1 -0
  66. package/dist/utils/cronToHuman.js +79 -0
  67. package/dist/utils/isDeferredTool.d.ts +19 -0
  68. package/dist/utils/isDeferredTool.d.ts.map +1 -0
  69. package/dist/utils/isDeferredTool.js +31 -0
  70. package/dist/utils/mcpUtils.d.ts.map +1 -1
  71. package/dist/utils/mcpUtils.js +1 -0
  72. package/dist/utils/parseCronExpression.d.ts +6 -0
  73. package/dist/utils/parseCronExpression.d.ts.map +1 -0
  74. package/dist/utils/parseCronExpression.js +74 -0
  75. package/dist/utils/worktreeSession.d.ts +26 -0
  76. package/dist/utils/worktreeSession.d.ts.map +1 -0
  77. package/dist/utils/worktreeSession.js +14 -0
  78. package/dist/utils/worktreeUtils.d.ts +42 -0
  79. package/dist/utils/worktreeUtils.d.ts.map +1 -0
  80. package/dist/utils/worktreeUtils.js +236 -0
  81. package/package.json +1 -1
  82. package/src/agent.ts +49 -12
  83. package/src/constants/tools.ts +3 -0
  84. package/src/index.ts +1 -0
  85. package/src/managers/aiManager.ts +73 -18
  86. package/src/managers/hookManager.ts +10 -0
  87. package/src/managers/mcpManager.ts +32 -6
  88. package/src/managers/permissionManager.ts +0 -42
  89. package/src/managers/slashCommandManager.ts +6 -0
  90. package/src/managers/toolManager.ts +47 -1
  91. package/src/prompts/index.ts +17 -3
  92. package/src/services/initializationService.ts +2 -41
  93. package/src/services/memory.ts +30 -17
  94. package/src/tools/cronCreateTool.ts +81 -8
  95. package/src/tools/cronDeleteTool.ts +7 -2
  96. package/src/tools/cronListTool.ts +7 -2
  97. package/src/tools/enterWorktreeTool.ts +183 -0
  98. package/src/tools/exitWorktreeTool.ts +242 -0
  99. package/src/tools/taskManagementTools.ts +4 -0
  100. package/src/tools/toolSearchTool.ts +228 -0
  101. package/src/tools/types.ts +19 -0
  102. package/src/tools/webFetchTool.ts +1 -0
  103. package/src/types/agent.ts +6 -0
  104. package/src/types/hooks.ts +4 -0
  105. package/src/utils/containerSetup.ts +7 -8
  106. package/src/utils/cronToHuman.ts +99 -0
  107. package/src/utils/isDeferredTool.ts +36 -0
  108. package/src/utils/mcpUtils.ts +1 -0
  109. package/src/utils/parseCronExpression.ts +78 -0
  110. package/src/utils/worktreeSession.ts +36 -0
  111. package/src/utils/worktreeUtils.ts +288 -0
@@ -1,20 +1,25 @@
1
1
  import { ToolPlugin, ToolResult, ToolContext } from "./types.js";
2
2
  import { CRON_LIST_TOOL_NAME } from "../constants/tools.js";
3
3
 
4
+ const CRON_LIST_DESCRIPTION = "List scheduled cron jobs";
5
+
6
+ const CRON_LIST_PROMPT = `List all cron jobs scheduled via CronCreate in this session.`;
7
+
4
8
  export const cronListTool: ToolPlugin = {
5
9
  name: CRON_LIST_TOOL_NAME,
10
+ shouldDefer: true,
6
11
  config: {
7
12
  type: "function",
8
13
  function: {
9
14
  name: CRON_LIST_TOOL_NAME,
10
- description:
11
- "List all cron jobs scheduled via CronCreate in this session.",
15
+ description: CRON_LIST_DESCRIPTION,
12
16
  parameters: {
13
17
  type: "object",
14
18
  properties: {},
15
19
  },
16
20
  },
17
21
  },
22
+ prompt: () => CRON_LIST_PROMPT,
18
23
  execute: async (
19
24
  _args: Record<string, unknown>,
20
25
  context: ToolContext,
@@ -0,0 +1,183 @@
1
+ /**
2
+ * EnterWorktree tool - creates an isolated git worktree and switches the session into it.
3
+ * Mirrors Claude Code's EnterWorktree tool behavior and prompt.
4
+ */
5
+
6
+ import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
7
+ import {
8
+ getCurrentWorktreeSession,
9
+ setCurrentWorktreeSession,
10
+ type WorktreeSession,
11
+ } from "../utils/worktreeSession.js";
12
+ import {
13
+ createWorktree,
14
+ validateWorktreeName,
15
+ generateWorktreeName,
16
+ } from "../utils/worktreeUtils.js";
17
+ import { getGitMainRepoRoot } from "../utils/gitUtils.js";
18
+ import { ENTER_WORKTREE_TOOL_NAME } from "../constants/tools.js";
19
+ import { logger } from "../utils/globalLogger.js";
20
+
21
+ export const ENTER_WORKTREE_TOOL_PROMPT = `Use this tool ONLY when the user explicitly asks to work in a worktree. This tool creates an isolated git worktree and switches the current session into it.
22
+
23
+ ## When to Use
24
+
25
+ - The user explicitly says "worktree" (e.g., "start a worktree", "work in a worktree", "create a worktree", "use a worktree")
26
+
27
+ ## When NOT to Use
28
+
29
+ - The user asks to create a branch, switch branches, or work on a different branch — use git commands instead
30
+ - The user asks to fix a bug or work on a feature — use normal git workflow unless they specifically mention worktrees
31
+ - Never use this tool unless the user explicitly mentions "worktree"
32
+
33
+ ## Requirements
34
+
35
+ - Must be in a git repository
36
+ - Must not already be in a worktree
37
+
38
+ ## Behavior
39
+
40
+ - Creates a new git worktree inside \`.wave/worktrees/\` with a new branch based on HEAD
41
+ - Switches the session's working directory to the new worktree
42
+ - Use ExitWorktree to leave the worktree mid-session (keep or remove). On session exit, if still in the worktree, the user will be prompted to keep or remove it
43
+
44
+ ## Parameters
45
+
46
+ - \`name\` (optional): A name for the worktree. Each "/"-separated segment may contain only letters, digits, dots, underscores, and dashes; max 64 chars total. A random name is generated if not provided.
47
+ `;
48
+
49
+ export const enterWorktreeTool: ToolPlugin = {
50
+ name: ENTER_WORKTREE_TOOL_NAME,
51
+ shouldDefer: true,
52
+ config: {
53
+ type: "function",
54
+ function: {
55
+ name: ENTER_WORKTREE_TOOL_NAME,
56
+ description: ENTER_WORKTREE_TOOL_PROMPT,
57
+ parameters: {
58
+ type: "object",
59
+ properties: {
60
+ name: {
61
+ type: "string",
62
+ description:
63
+ 'Optional name for the worktree. Each "/"-separated segment may contain only letters, digits, dots, underscores, and dashes; max 64 chars total. A random name is generated if not provided.',
64
+ },
65
+ },
66
+ },
67
+ },
68
+ },
69
+ prompt: () => ENTER_WORKTREE_TOOL_PROMPT,
70
+
71
+ async execute(
72
+ args: Record<string, unknown>,
73
+ context: ToolContext,
74
+ ): Promise<ToolResult> {
75
+ // Validate not already in a worktree created by this session
76
+ if (getCurrentWorktreeSession()) {
77
+ return {
78
+ success: false,
79
+ content:
80
+ "Already in a worktree session. Use ExitWorktree to leave before creating a new one.",
81
+ error: "Already in a worktree session",
82
+ };
83
+ }
84
+
85
+ const name = (args.name as string) || generateWorktreeName();
86
+
87
+ // Validate the worktree name
88
+ try {
89
+ validateWorktreeName(name);
90
+ } catch (e) {
91
+ return {
92
+ success: false,
93
+ content: `Invalid worktree name: ${(e as Error).message}`,
94
+ error: `Invalid worktree name: ${(e as Error).message}`,
95
+ };
96
+ }
97
+
98
+ // Resolve to main repo root so worktree creation works from within a subdirectory
99
+ const mainRepoRoot = getGitMainRepoRoot(context.workdir);
100
+ if (!mainRepoRoot) {
101
+ return {
102
+ success: false,
103
+ content:
104
+ "Cannot create a worktree: not in a git repository. Configure WorktreeCreate and WorktreeRemove hooks in settings.json to use worktree isolation with other VCS systems.",
105
+ error: "Not in a git repository",
106
+ };
107
+ }
108
+
109
+ // Create the worktree (captures originalHeadCommit internally)
110
+ const worktreeInfo = createWorktree(name, mainRepoRoot);
111
+
112
+ // Build session state
113
+ const session: WorktreeSession = {
114
+ originalCwd: context.workdir,
115
+ worktreePath: worktreeInfo.path,
116
+ worktreeBranch: worktreeInfo.branch,
117
+ worktreeName: worktreeInfo.name,
118
+ isNew: worktreeInfo.isNew,
119
+ repoRoot: worktreeInfo.repoRoot,
120
+ originalHeadCommit: worktreeInfo.originalHeadCommit,
121
+ };
122
+
123
+ // Set module-level session state
124
+ setCurrentWorktreeSession(session);
125
+
126
+ // Update CWD via AIManager
127
+ const aiManager = context.aiManager;
128
+ if (aiManager) {
129
+ aiManager.setWorkdir(worktreeInfo.path);
130
+ }
131
+
132
+ // Also update the container's Workdir entry
133
+ // (Container is not directly accessible from ToolContext, but AIManager.setWorkdir
134
+ // handles both its internal field and process.chdir)
135
+
136
+ // Trigger WorktreeCreate hook if worktree is new
137
+ let hookTriggered = false;
138
+ if (session.isNew && context.hookManager) {
139
+ try {
140
+ const hookResults = await context.hookManager.executeHooks(
141
+ "WorktreeCreate",
142
+ {
143
+ event: "WorktreeCreate",
144
+ projectDir: worktreeInfo.path,
145
+ timestamp: new Date(),
146
+ sessionId: context.sessionId ?? "",
147
+ transcriptPath: context.messageManager?.getTranscriptPath() ?? "",
148
+ cwd: worktreeInfo.path,
149
+ worktreeName: worktreeInfo.name,
150
+ env: Object.fromEntries(
151
+ Object.entries(process.env).filter((e) => e[1] !== undefined),
152
+ ) as Record<string, string>,
153
+ },
154
+ );
155
+
156
+ if (context.messageManager) {
157
+ context.hookManager.processHookResults(
158
+ "WorktreeCreate",
159
+ hookResults,
160
+ context.messageManager,
161
+ );
162
+ }
163
+
164
+ hookTriggered = true;
165
+ } catch (error) {
166
+ // Non-blocking: log but don't fail the tool
167
+ logger?.warn("WorktreeCreate hooks execution failed:", error);
168
+ }
169
+ }
170
+
171
+ const branchInfo = worktreeInfo.branch
172
+ ? ` on branch ${worktreeInfo.branch}`
173
+ : "";
174
+ const hookInfo = hookTriggered
175
+ ? " WorktreeCreate hooks were executed."
176
+ : "";
177
+
178
+ return {
179
+ success: true,
180
+ content: `Created worktree at ${worktreeInfo.path}${branchInfo}. The session is now working in the worktree. Use ExitWorktree to leave mid-session, or exit the session to be prompted.${hookInfo}`,
181
+ };
182
+ },
183
+ };
@@ -0,0 +1,242 @@
1
+ /**
2
+ * ExitWorktree tool - exits a worktree session and returns to the original directory.
3
+ * Mirrors Claude Code's ExitWorktree tool behavior and prompt.
4
+ */
5
+
6
+ import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
7
+ import {
8
+ getCurrentWorktreeSession,
9
+ setCurrentWorktreeSession,
10
+ } from "../utils/worktreeSession.js";
11
+ import {
12
+ removeWorktree,
13
+ countWorktreeChanges,
14
+ } from "../utils/worktreeUtils.js";
15
+ import { EXIT_WORKTREE_TOOL_NAME } from "../constants/tools.js";
16
+ import { logger } from "../utils/globalLogger.js";
17
+
18
+ export const EXIT_WORKTREE_TOOL_PROMPT = `Exit a worktree session created by EnterWorktree and return the session to the original working directory.
19
+
20
+ ## Scope
21
+
22
+ This tool ONLY operates on worktrees created by EnterWorktree in this session. It will NOT touch:
23
+ - Worktrees you created manually with \`git worktree add\`
24
+ - Worktrees from a previous session (even if created by EnterWorktree then)
25
+ - The directory you're in if EnterWorktree was never called
26
+
27
+ If called outside an EnterWorktree session, the tool is a **no-op**: it reports that no worktree session is active and takes no action. Filesystem state is unchanged.
28
+
29
+ ## When to Use
30
+
31
+ - The user explicitly asks to "exit the worktree", "leave the worktree", "go back", or otherwise end the worktree session
32
+ - Do NOT call this proactively — only when the user asks
33
+
34
+ ## Parameters
35
+
36
+ - \`action\` (required): \`"keep"\` or \`"remove"\`
37
+ - \`"keep"\` — leave the worktree directory and branch intact on disk. Use this if the user wants to come back to the work later, or if there are changes to preserve.
38
+ - \`"remove"\` — delete the worktree directory and its branch. Use this for a clean exit when the work is done or abandoned.
39
+ - \`discard_changes\` (optional, default false): only meaningful with \`action: "remove"\`. If the worktree has uncommitted files or commits not on the original branch, the tool will REFUSE to remove it unless this is set to \`true\`. If the tool returns an error listing changes, confirm with the user before re-invoking with \`discard_changes: true\`.
40
+
41
+ ## Behavior
42
+
43
+ - Restores the session's working directory to where it was before EnterWorktree
44
+ - If action is "remove": deletes the worktree directory and branch
45
+ - Once exited, EnterWorktree can be called again to create a fresh worktree
46
+ `;
47
+
48
+ export const exitWorktreeTool: ToolPlugin = {
49
+ name: EXIT_WORKTREE_TOOL_NAME,
50
+ shouldDefer: true,
51
+ config: {
52
+ type: "function",
53
+ function: {
54
+ name: EXIT_WORKTREE_TOOL_NAME,
55
+ description: EXIT_WORKTREE_TOOL_PROMPT,
56
+ parameters: {
57
+ type: "object",
58
+ properties: {
59
+ action: {
60
+ type: "string",
61
+ enum: ["keep", "remove"],
62
+ description:
63
+ '"keep" leaves the worktree and branch on disk; "remove" deletes both.',
64
+ },
65
+ discard_changes: {
66
+ type: "boolean",
67
+ description:
68
+ 'Required true when action is "remove" and the worktree has uncommitted files or unmerged commits. The tool will refuse and list them otherwise.',
69
+ },
70
+ },
71
+ required: ["action"],
72
+ },
73
+ },
74
+ },
75
+ prompt: () => EXIT_WORKTREE_TOOL_PROMPT,
76
+
77
+ async execute(
78
+ args: Record<string, unknown>,
79
+ context: ToolContext,
80
+ ): Promise<ToolResult> {
81
+ const action = args.action as "keep" | "remove" | undefined;
82
+ const discardChanges = (args.discard_changes as boolean) ?? false;
83
+
84
+ if (!action) {
85
+ return {
86
+ success: false,
87
+ content:
88
+ 'Missing required parameter: "action" must be "keep" or "remove".',
89
+ error: 'Missing required parameter: "action"',
90
+ };
91
+ }
92
+
93
+ // Validate: must be in an active worktree session
94
+ const session = getCurrentWorktreeSession();
95
+ if (!session) {
96
+ return {
97
+ success: false,
98
+ content:
99
+ "No-op: there is no active EnterWorktree session to exit. This tool only operates on worktrees created by EnterWorktree in the current session — it will not touch worktrees created manually or in a previous session. No filesystem changes were made.",
100
+ error: "No active worktree session",
101
+ };
102
+ }
103
+
104
+ // Safety check for removal with changes
105
+ if (action === "remove" && !discardChanges) {
106
+ const summary = countWorktreeChanges(
107
+ session.worktreePath,
108
+ session.originalHeadCommit,
109
+ );
110
+ if (summary === null) {
111
+ return {
112
+ success: false,
113
+ content: `Could not verify worktree state at ${session.worktreePath}. Refusing to remove without explicit confirmation. Re-invoke with discard_changes: true to proceed — or use action: "keep" to preserve the worktree.`,
114
+ error: "Could not verify worktree state",
115
+ };
116
+ }
117
+ const { changedFiles, commits } = summary;
118
+ if (changedFiles > 0 || commits > 0) {
119
+ const parts: string[] = [];
120
+ if (changedFiles > 0) {
121
+ parts.push(
122
+ `${changedFiles} uncommitted ${changedFiles === 1 ? "file" : "files"}`,
123
+ );
124
+ }
125
+ if (commits > 0) {
126
+ parts.push(
127
+ `${commits} ${commits === 1 ? "commit" : "commits"} on ${session.worktreeBranch ?? "the worktree branch"}`,
128
+ );
129
+ }
130
+ return {
131
+ success: false,
132
+ content: `Worktree has ${parts.join(" and ")}. Removing will discard this work permanently. Confirm with the user, then re-invoke with discard_changes: true — or use action: "keep" to preserve the worktree.`,
133
+ error: "Worktree has uncommitted changes",
134
+ };
135
+ }
136
+ }
137
+
138
+ // Capture info before clearing session
139
+ const { originalCwd, worktreePath, worktreeBranch } = session;
140
+
141
+ if (action === "keep") {
142
+ // Clear session state
143
+ setCurrentWorktreeSession(null);
144
+
145
+ // Restore CWD
146
+ const aiManager = context.aiManager;
147
+ if (aiManager) {
148
+ aiManager.setWorkdir(originalCwd);
149
+ }
150
+
151
+ return {
152
+ success: true,
153
+ content: `Exited worktree. Your work is preserved at ${worktreePath}${worktreeBranch ? ` on branch ${worktreeBranch}` : ""}. Session is now back in ${originalCwd}.`,
154
+ };
155
+ }
156
+
157
+ // action === "remove"
158
+ const worktreeInfo = {
159
+ name: session.worktreeName,
160
+ path: worktreePath,
161
+ branch: worktreeBranch,
162
+ repoRoot: session.repoRoot,
163
+ isNew: session.isNew,
164
+ };
165
+
166
+ // Count changes BEFORE removing the worktree (directory will be gone after)
167
+ const summary = countWorktreeChanges(
168
+ worktreePath,
169
+ session.originalHeadCommit,
170
+ ) ?? { changedFiles: 0, commits: 0 };
171
+
172
+ removeWorktree(worktreeInfo);
173
+
174
+ // Clear session state
175
+ setCurrentWorktreeSession(null);
176
+
177
+ // Restore CWD
178
+ const aiManager = context.aiManager;
179
+ if (aiManager) {
180
+ aiManager.setWorkdir(originalCwd);
181
+ }
182
+
183
+ // Trigger WorktreeRemove hook (non-blocking)
184
+ let hookTriggered = false;
185
+ if (context.hookManager) {
186
+ try {
187
+ const hookResults = await context.hookManager.executeHooks(
188
+ "WorktreeRemove",
189
+ {
190
+ event: "WorktreeRemove",
191
+ projectDir: originalCwd,
192
+ timestamp: new Date(),
193
+ sessionId: context.sessionId ?? "",
194
+ transcriptPath: context.messageManager?.getTranscriptPath() ?? "",
195
+ cwd: originalCwd,
196
+ worktreePath,
197
+ env: Object.fromEntries(
198
+ Object.entries(process.env).filter((e) => e[1] !== undefined),
199
+ ) as Record<string, string>,
200
+ },
201
+ );
202
+
203
+ if (context.messageManager) {
204
+ context.hookManager.processHookResults(
205
+ "WorktreeRemove",
206
+ hookResults,
207
+ context.messageManager,
208
+ );
209
+ }
210
+
211
+ hookTriggered = true;
212
+ } catch (error) {
213
+ // Non-blocking: log but don't fail the tool
214
+ logger?.warn("WorktreeRemove hooks execution failed:", error);
215
+ }
216
+ }
217
+
218
+ const discardParts: string[] = [];
219
+ if (summary.commits > 0) {
220
+ discardParts.push(
221
+ `${summary.commits} ${summary.commits === 1 ? "commit" : "commits"}`,
222
+ );
223
+ }
224
+ if (summary.changedFiles > 0) {
225
+ discardParts.push(
226
+ `${summary.changedFiles} uncommitted ${summary.changedFiles === 1 ? "file" : "files"}`,
227
+ );
228
+ }
229
+ const discardNote =
230
+ discardParts.length > 0
231
+ ? ` Discarded ${discardParts.join(" and ")}.`
232
+ : "";
233
+ const hookNote = hookTriggered
234
+ ? " WorktreeRemove hooks were executed."
235
+ : "";
236
+
237
+ return {
238
+ success: true,
239
+ content: `Exited and removed worktree at ${worktreePath}.${discardNote} Session is now back in ${originalCwd}.${hookNote}`,
240
+ };
241
+ },
242
+ };
@@ -9,6 +9,7 @@ import {
9
9
 
10
10
  export const taskCreateTool: ToolPlugin = {
11
11
  name: TASK_CREATE_TOOL_NAME,
12
+ shouldDefer: true,
12
13
  config: {
13
14
  type: "function",
14
15
  function: {
@@ -143,6 +144,7 @@ NOTE that you should not use this tool if there is only one trivial task to do.
143
144
 
144
145
  export const taskGetTool: ToolPlugin = {
145
146
  name: TASK_GET_TOOL_NAME,
147
+ shouldDefer: true,
146
148
  config: {
147
149
  type: "function",
148
150
  function: {
@@ -202,6 +204,7 @@ Returns full task details:
202
204
 
203
205
  export const taskUpdateTool: ToolPlugin = {
204
206
  name: TASK_UPDATE_TOOL_NAME,
207
+ shouldDefer: true,
205
208
  config: {
206
209
  type: "function",
207
210
  function: {
@@ -556,6 +559,7 @@ Set up task dependencies:
556
559
 
557
560
  export const taskListTool: ToolPlugin = {
558
561
  name: TASK_LIST_TOOL_NAME,
562
+ shouldDefer: true,
559
563
  config: {
560
564
  type: "function",
561
565
  function: {