wave-agent-sdk 1.0.4 → 1.0.6
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.
- package/dist/agent.d.ts +23 -1
- package/dist/agent.js +34 -4
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -8
- package/dist/managers/messageManager.d.ts +1 -3
- package/dist/managers/messageManager.js +19 -9
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/services/session.js +2 -9
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/index.d.ts +3 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +10 -1
- package/dist/utils/messageOperations.js +19 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1180
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -922
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -641
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
|
@@ -1,190 +0,0 @@
|
|
|
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 { type WorktreeSession } from "../utils/worktreeSession.js";
|
|
8
|
-
import {
|
|
9
|
-
createWorktree,
|
|
10
|
-
validateWorktreeName,
|
|
11
|
-
generateWorktreeName,
|
|
12
|
-
performPostCreationSetup,
|
|
13
|
-
} from "../utils/worktreeUtils.js";
|
|
14
|
-
import { getGitMainRepoRoot } from "../utils/gitUtils.js";
|
|
15
|
-
import { ENTER_WORKTREE_TOOL_NAME } from "../constants/tools.js";
|
|
16
|
-
import { logger } from "../utils/globalLogger.js";
|
|
17
|
-
|
|
18
|
-
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.
|
|
19
|
-
|
|
20
|
-
## When to Use
|
|
21
|
-
|
|
22
|
-
- The user explicitly says "worktree" (e.g., "start a worktree", "work in a worktree", "create a worktree", "use a worktree")
|
|
23
|
-
|
|
24
|
-
## When NOT to Use
|
|
25
|
-
|
|
26
|
-
- The user asks to create a branch, switch branches, or work on a different branch — use git commands instead
|
|
27
|
-
- The user asks to fix a bug or work on a feature — use normal git workflow unless they specifically mention worktrees
|
|
28
|
-
- Never use this tool unless the user explicitly mentions "worktree"
|
|
29
|
-
|
|
30
|
-
## Requirements
|
|
31
|
-
|
|
32
|
-
- Must be in a git repository
|
|
33
|
-
- Must not already be in a worktree
|
|
34
|
-
|
|
35
|
-
## Behavior
|
|
36
|
-
|
|
37
|
-
- Creates a new git worktree inside \`.wave/worktrees/\` with a new branch based on HEAD
|
|
38
|
-
- Switches the session's working directory to the new worktree
|
|
39
|
-
- 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
|
|
40
|
-
|
|
41
|
-
## Parameters
|
|
42
|
-
|
|
43
|
-
- \`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.
|
|
44
|
-
`;
|
|
45
|
-
|
|
46
|
-
export const enterWorktreeTool: ToolPlugin = {
|
|
47
|
-
name: ENTER_WORKTREE_TOOL_NAME,
|
|
48
|
-
config: {
|
|
49
|
-
type: "function",
|
|
50
|
-
function: {
|
|
51
|
-
name: ENTER_WORKTREE_TOOL_NAME,
|
|
52
|
-
description: ENTER_WORKTREE_TOOL_PROMPT,
|
|
53
|
-
parameters: {
|
|
54
|
-
type: "object",
|
|
55
|
-
properties: {
|
|
56
|
-
name: {
|
|
57
|
-
type: "string",
|
|
58
|
-
description:
|
|
59
|
-
'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.',
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
prompt: () => ENTER_WORKTREE_TOOL_PROMPT,
|
|
66
|
-
|
|
67
|
-
async execute(
|
|
68
|
-
args: Record<string, unknown>,
|
|
69
|
-
context: ToolContext,
|
|
70
|
-
): Promise<ToolResult> {
|
|
71
|
-
// Validate not already in a worktree created by this session
|
|
72
|
-
if (context.aiManager?.getWorktreeSession()) {
|
|
73
|
-
return {
|
|
74
|
-
success: false,
|
|
75
|
-
content:
|
|
76
|
-
"Already in a worktree session. Use ExitWorktree to leave before creating a new one.",
|
|
77
|
-
error: "Already in a worktree session",
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const name = (args.name as string) || generateWorktreeName();
|
|
82
|
-
|
|
83
|
-
// Validate the worktree name
|
|
84
|
-
try {
|
|
85
|
-
validateWorktreeName(name);
|
|
86
|
-
} catch (e) {
|
|
87
|
-
return {
|
|
88
|
-
success: false,
|
|
89
|
-
content: `Invalid worktree name: ${(e as Error).message}`,
|
|
90
|
-
error: `Invalid worktree name: ${(e as Error).message}`,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Resolve to main repo root so worktree creation works from within a subdirectory
|
|
95
|
-
const mainRepoRoot = getGitMainRepoRoot(context.workdir);
|
|
96
|
-
if (!mainRepoRoot) {
|
|
97
|
-
return {
|
|
98
|
-
success: false,
|
|
99
|
-
content:
|
|
100
|
-
"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.",
|
|
101
|
-
error: "Not in a git repository",
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Create the worktree (captures originalHeadCommit internally)
|
|
106
|
-
const baseRef = context.aiManager?.getWorktreeBaseRef?.();
|
|
107
|
-
const worktreeInfo = createWorktree(name, mainRepoRoot, { baseRef });
|
|
108
|
-
|
|
109
|
-
// Copy local settings (.wave/settings.local.json) and gitignored project
|
|
110
|
-
// files (.worktreeinclude, e.g. .env/.mcp.json) into a new worktree —
|
|
111
|
-
// mirrors the CLI createWorktree path. Best-effort, never fails the tool.
|
|
112
|
-
if (worktreeInfo.isNew) {
|
|
113
|
-
try {
|
|
114
|
-
await performPostCreationSetup(
|
|
115
|
-
worktreeInfo.path,
|
|
116
|
-
worktreeInfo.repoRoot,
|
|
117
|
-
);
|
|
118
|
-
} catch (error) {
|
|
119
|
-
logger?.warn("Worktree post-creation setup failed:", error);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Build session state
|
|
124
|
-
const session: WorktreeSession = {
|
|
125
|
-
originalCwd: context.workdir,
|
|
126
|
-
worktreePath: worktreeInfo.path,
|
|
127
|
-
worktreeBranch: worktreeInfo.branch,
|
|
128
|
-
worktreeName: worktreeInfo.name,
|
|
129
|
-
isNew: worktreeInfo.isNew,
|
|
130
|
-
repoRoot: worktreeInfo.repoRoot,
|
|
131
|
-
originalHeadCommit: worktreeInfo.originalHeadCommit,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
// Set per-session worktree state and update CWD via AIManager
|
|
135
|
-
const aiManager = context.aiManager;
|
|
136
|
-
if (aiManager) {
|
|
137
|
-
aiManager.setWorktreeSession(session);
|
|
138
|
-
aiManager.setWorkdir(worktreeInfo.path);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Trigger WorktreeCreate hook if worktree is new
|
|
142
|
-
let hookTriggered = false;
|
|
143
|
-
if (session.isNew && context.hookManager) {
|
|
144
|
-
try {
|
|
145
|
-
const hookResults = await context.hookManager.executeHooks(
|
|
146
|
-
"WorktreeCreate",
|
|
147
|
-
{
|
|
148
|
-
event: "WorktreeCreate",
|
|
149
|
-
projectDir: worktreeInfo.path,
|
|
150
|
-
timestamp: new Date(),
|
|
151
|
-
sessionId: context.sessionId ?? "",
|
|
152
|
-
transcriptPath: context.messageManager?.getTranscriptPath?.() ?? "",
|
|
153
|
-
cwd: worktreeInfo.path,
|
|
154
|
-
worktreeName: worktreeInfo.name,
|
|
155
|
-
env: Object.fromEntries(
|
|
156
|
-
Object.entries(context.sessionEnv ?? process.env).filter(
|
|
157
|
-
(e) => e[1] !== undefined,
|
|
158
|
-
),
|
|
159
|
-
) as Record<string, string>,
|
|
160
|
-
},
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
if (context.messageManager) {
|
|
164
|
-
context.hookManager.processHookResults(
|
|
165
|
-
"WorktreeCreate",
|
|
166
|
-
hookResults,
|
|
167
|
-
context.messageManager,
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
hookTriggered = true;
|
|
172
|
-
} catch (error) {
|
|
173
|
-
// Non-blocking: log but don't fail the tool
|
|
174
|
-
logger?.warn("WorktreeCreate hooks execution failed:", error);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const branchInfo = worktreeInfo.branch
|
|
179
|
-
? ` on branch ${worktreeInfo.branch}`
|
|
180
|
-
: "";
|
|
181
|
-
const hookInfo = hookTriggered
|
|
182
|
-
? " WorktreeCreate hooks were executed."
|
|
183
|
-
: "";
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
success: true,
|
|
187
|
-
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}`,
|
|
188
|
-
};
|
|
189
|
-
},
|
|
190
|
-
};
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { readFile } from "fs/promises";
|
|
2
|
-
import { logger } from "../utils/globalLogger.js";
|
|
3
|
-
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
4
|
-
import { EXIT_PLAN_MODE_TOOL_NAME } from "../constants/tools.js";
|
|
5
|
-
import { OPERATION_CANCELLED_BY_USER } from "../types/permissions.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Exit Plan Mode Tool Plugin
|
|
9
|
-
*/
|
|
10
|
-
export const exitPlanModeTool: ToolPlugin = {
|
|
11
|
-
name: EXIT_PLAN_MODE_TOOL_NAME,
|
|
12
|
-
config: {
|
|
13
|
-
type: "function",
|
|
14
|
-
function: {
|
|
15
|
-
name: EXIT_PLAN_MODE_TOOL_NAME,
|
|
16
|
-
description: "Prompts the user to exit plan mode and start coding",
|
|
17
|
-
parameters: {
|
|
18
|
-
type: "object",
|
|
19
|
-
properties: {},
|
|
20
|
-
required: [],
|
|
21
|
-
additionalProperties: false,
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
prompt:
|
|
26
|
-
() => `Use this tool when you are in plan mode and have finished writing your plan to the plan file and are ready for user approval.
|
|
27
|
-
|
|
28
|
-
## How This Tool Works
|
|
29
|
-
- You should have already written your plan to the plan file specified in the plan mode system message
|
|
30
|
-
- This tool does NOT take the plan content as a parameter - it will read the plan from the file you wrote
|
|
31
|
-
- This tool simply signals that you're done planning and ready for the user to review and approve
|
|
32
|
-
- The user will see the contents of your plan file when they review it
|
|
33
|
-
|
|
34
|
-
## When to Use This Tool
|
|
35
|
-
IMPORTANT: Only use this tool when the task requires planning the implementation steps of a task that requires writing code. For research tasks where you're gathering information, searching files, reading files or in general trying to understand the codebase - do NOT use this tool.
|
|
36
|
-
|
|
37
|
-
## Before Using This Tool
|
|
38
|
-
Ensure your plan is complete and unambiguous:
|
|
39
|
-
- If you have unresolved questions about requirements or approach, use AskUserQuestion first (in earlier phases)
|
|
40
|
-
- Once your plan is finalized, use THIS tool to request approval
|
|
41
|
-
|
|
42
|
-
**Important:** Do NOT use AskUserQuestion to ask "Is this plan okay?" or "Should I proceed?" - that's exactly what THIS tool does. ExitPlanMode inherently requests user approval of your plan.
|
|
43
|
-
|
|
44
|
-
## Examples
|
|
45
|
-
|
|
46
|
-
1. Initial task: "Search for and understand the implementation of vim mode in the codebase" - Do not use the exit plan mode tool because you are not planning the implementation steps of a task.
|
|
47
|
-
2. Initial task: "Help me implement yank mode for vim" - Use the exit plan mode tool after you have finished planning the implementation steps of the task.
|
|
48
|
-
3. Initial task: "Add a new feature to handle user authentication" - If unsure about auth method (OAuth, JWT, etc.), use AskUserQuestion first, then use exit plan mode tool after clarifying the approach.
|
|
49
|
-
`,
|
|
50
|
-
execute: async (
|
|
51
|
-
_args: Record<string, unknown>,
|
|
52
|
-
context: ToolContext,
|
|
53
|
-
): Promise<ToolResult> => {
|
|
54
|
-
try {
|
|
55
|
-
// Runtime guard: tool is always visible but only works in plan mode
|
|
56
|
-
if (context.permissionMode !== "plan") {
|
|
57
|
-
return {
|
|
58
|
-
success: false,
|
|
59
|
-
content:
|
|
60
|
-
"Not in plan mode. ExitPlanMode can only be used when actively in plan mode.",
|
|
61
|
-
error: "Not in plan mode",
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!context.permissionManager) {
|
|
66
|
-
return {
|
|
67
|
-
success: false,
|
|
68
|
-
content: "",
|
|
69
|
-
error: "Permission manager is not available",
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const planFilePath = context.permissionManager.getPlanFilePath();
|
|
74
|
-
if (!planFilePath) {
|
|
75
|
-
return {
|
|
76
|
-
success: false,
|
|
77
|
-
content: "",
|
|
78
|
-
error: "Plan file path is not set",
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
let planContent = "";
|
|
83
|
-
try {
|
|
84
|
-
planContent = await readFile(planFilePath, "utf-8");
|
|
85
|
-
} catch (error) {
|
|
86
|
-
return {
|
|
87
|
-
success: false,
|
|
88
|
-
content: "",
|
|
89
|
-
error: `Failed to read plan file: ${error instanceof Error ? error.message : String(error)}`,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Permission check triggers the 3-option UI
|
|
94
|
-
const permissionContext = context.permissionManager.createContext(
|
|
95
|
-
EXIT_PLAN_MODE_TOOL_NAME,
|
|
96
|
-
context.permissionMode || "plan",
|
|
97
|
-
context.canUseToolCallback,
|
|
98
|
-
{},
|
|
99
|
-
context.toolCallId,
|
|
100
|
-
planContent,
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const permissionResult =
|
|
104
|
-
await context.permissionManager.checkPermission(permissionContext);
|
|
105
|
-
|
|
106
|
-
if (permissionResult.behavior === "deny") {
|
|
107
|
-
if (permissionResult.message === OPERATION_CANCELLED_BY_USER) {
|
|
108
|
-
return {
|
|
109
|
-
success: false,
|
|
110
|
-
content: OPERATION_CANCELLED_BY_USER,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
success: false,
|
|
115
|
-
content: `Please update your proposal based on the following user feedback: ${permissionResult.message || "Plan rejected by user"}`,
|
|
116
|
-
error: permissionResult.message ? undefined : "Plan rejected by user",
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
context.permissionManager.setHasExitedPlanMode(true);
|
|
121
|
-
context.permissionManager.setNeedsPlanModeExitAttachment(true);
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
success: true,
|
|
125
|
-
content: "Plan approved. Exiting plan mode.",
|
|
126
|
-
shortResult: "Plan approved",
|
|
127
|
-
};
|
|
128
|
-
} catch (error) {
|
|
129
|
-
const errorMessage =
|
|
130
|
-
error instanceof Error ? error.message : String(error);
|
|
131
|
-
logger.error(`ExitPlanMode tool error: ${errorMessage}`);
|
|
132
|
-
return {
|
|
133
|
-
success: false,
|
|
134
|
-
content: "",
|
|
135
|
-
error: errorMessage,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
};
|
|
@@ -1,236 +0,0 @@
|
|
|
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
|
-
removeWorktree,
|
|
9
|
-
countWorktreeChanges,
|
|
10
|
-
} from "../utils/worktreeUtils.js";
|
|
11
|
-
import { EXIT_WORKTREE_TOOL_NAME } from "../constants/tools.js";
|
|
12
|
-
import { logger } from "../utils/globalLogger.js";
|
|
13
|
-
|
|
14
|
-
export const EXIT_WORKTREE_TOOL_PROMPT = `Exit a worktree session created by EnterWorktree and return the session to the original working directory.
|
|
15
|
-
|
|
16
|
-
## Scope
|
|
17
|
-
|
|
18
|
-
This tool ONLY operates on worktrees created by EnterWorktree in this session. It will NOT touch:
|
|
19
|
-
- Worktrees you created manually with \`git worktree add\`
|
|
20
|
-
- Worktrees from a previous session (even if created by EnterWorktree then)
|
|
21
|
-
- The directory you're in if EnterWorktree was never called
|
|
22
|
-
|
|
23
|
-
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.
|
|
24
|
-
|
|
25
|
-
## When to Use
|
|
26
|
-
|
|
27
|
-
- The user explicitly asks to "exit the worktree", "leave the worktree", "go back", or otherwise end the worktree session
|
|
28
|
-
- Do NOT call this proactively — only when the user asks
|
|
29
|
-
|
|
30
|
-
## Parameters
|
|
31
|
-
|
|
32
|
-
- \`action\` (required): \`"keep"\` or \`"remove"\`
|
|
33
|
-
- \`"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.
|
|
34
|
-
- \`"remove"\` — delete the worktree directory and its branch. Use this for a clean exit when the work is done or abandoned.
|
|
35
|
-
- \`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\`.
|
|
36
|
-
|
|
37
|
-
## Behavior
|
|
38
|
-
|
|
39
|
-
- Restores the session's working directory to where it was before EnterWorktree
|
|
40
|
-
- If action is "remove": deletes the worktree directory and branch
|
|
41
|
-
- Once exited, EnterWorktree can be called again to create a fresh worktree
|
|
42
|
-
`;
|
|
43
|
-
|
|
44
|
-
export const exitWorktreeTool: ToolPlugin = {
|
|
45
|
-
name: EXIT_WORKTREE_TOOL_NAME,
|
|
46
|
-
config: {
|
|
47
|
-
type: "function",
|
|
48
|
-
function: {
|
|
49
|
-
name: EXIT_WORKTREE_TOOL_NAME,
|
|
50
|
-
description: EXIT_WORKTREE_TOOL_PROMPT,
|
|
51
|
-
parameters: {
|
|
52
|
-
type: "object",
|
|
53
|
-
properties: {
|
|
54
|
-
action: {
|
|
55
|
-
type: "string",
|
|
56
|
-
enum: ["keep", "remove"],
|
|
57
|
-
description:
|
|
58
|
-
'"keep" leaves the worktree and branch on disk; "remove" deletes both.',
|
|
59
|
-
},
|
|
60
|
-
discard_changes: {
|
|
61
|
-
type: "boolean",
|
|
62
|
-
description:
|
|
63
|
-
'Required true when action is "remove" and the worktree has uncommitted files or unmerged commits. The tool will refuse and list them otherwise.',
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
required: ["action"],
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
prompt: () => EXIT_WORKTREE_TOOL_PROMPT,
|
|
71
|
-
|
|
72
|
-
async execute(
|
|
73
|
-
args: Record<string, unknown>,
|
|
74
|
-
context: ToolContext,
|
|
75
|
-
): Promise<ToolResult> {
|
|
76
|
-
const action = args.action as "keep" | "remove" | undefined;
|
|
77
|
-
const discardChanges = (args.discard_changes as boolean) ?? false;
|
|
78
|
-
|
|
79
|
-
if (!action) {
|
|
80
|
-
return {
|
|
81
|
-
success: false,
|
|
82
|
-
content:
|
|
83
|
-
'Missing required parameter: "action" must be "keep" or "remove".',
|
|
84
|
-
error: 'Missing required parameter: "action"',
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Validate: must be in an active worktree session (for this session)
|
|
89
|
-
const session = context.aiManager?.getWorktreeSession();
|
|
90
|
-
if (!session) {
|
|
91
|
-
return {
|
|
92
|
-
success: false,
|
|
93
|
-
content:
|
|
94
|
-
"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.",
|
|
95
|
-
error: "No active worktree session",
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Safety check for removal with changes
|
|
100
|
-
if (action === "remove" && !discardChanges) {
|
|
101
|
-
const summary = countWorktreeChanges(
|
|
102
|
-
session.worktreePath,
|
|
103
|
-
session.originalHeadCommit,
|
|
104
|
-
);
|
|
105
|
-
if (summary === null) {
|
|
106
|
-
return {
|
|
107
|
-
success: false,
|
|
108
|
-
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.`,
|
|
109
|
-
error: "Could not verify worktree state",
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
const { changedFiles, commits } = summary;
|
|
113
|
-
if (changedFiles > 0 || commits > 0) {
|
|
114
|
-
const parts: string[] = [];
|
|
115
|
-
if (changedFiles > 0) {
|
|
116
|
-
parts.push(
|
|
117
|
-
`${changedFiles} uncommitted ${changedFiles === 1 ? "file" : "files"}`,
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
if (commits > 0) {
|
|
121
|
-
parts.push(
|
|
122
|
-
`${commits} ${commits === 1 ? "commit" : "commits"} on ${session.worktreeBranch ?? "the worktree branch"}`,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
return {
|
|
126
|
-
success: false,
|
|
127
|
-
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.`,
|
|
128
|
-
error: "Worktree has uncommitted changes",
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Capture info before clearing session
|
|
134
|
-
const { originalCwd, worktreePath, worktreeBranch } = session;
|
|
135
|
-
|
|
136
|
-
if (action === "keep") {
|
|
137
|
-
// Clear session state
|
|
138
|
-
const aiManager = context.aiManager;
|
|
139
|
-
if (aiManager) {
|
|
140
|
-
aiManager.setWorktreeSession(null);
|
|
141
|
-
aiManager.setWorkdir(originalCwd);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
success: true,
|
|
146
|
-
content: `Exited worktree. Your work is preserved at ${worktreePath}${worktreeBranch ? ` on branch ${worktreeBranch}` : ""}. Session is now back in ${originalCwd}.`,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// action === "remove"
|
|
151
|
-
const worktreeInfo = {
|
|
152
|
-
name: session.worktreeName,
|
|
153
|
-
path: worktreePath,
|
|
154
|
-
branch: worktreeBranch,
|
|
155
|
-
repoRoot: session.repoRoot,
|
|
156
|
-
isNew: session.isNew,
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// Count changes BEFORE removing the worktree (directory will be gone after)
|
|
160
|
-
const summary = countWorktreeChanges(
|
|
161
|
-
worktreePath,
|
|
162
|
-
session.originalHeadCommit,
|
|
163
|
-
) ?? { changedFiles: 0, commits: 0 };
|
|
164
|
-
|
|
165
|
-
// Trigger WorktreeRemove hook (non-blocking) BEFORE git removal so hooks
|
|
166
|
-
// can still read files inside the worktree to clean up external resources.
|
|
167
|
-
let hookTriggered = false;
|
|
168
|
-
if (context.hookManager) {
|
|
169
|
-
try {
|
|
170
|
-
const hookResults = await context.hookManager.executeHooks(
|
|
171
|
-
"WorktreeRemove",
|
|
172
|
-
{
|
|
173
|
-
event: "WorktreeRemove",
|
|
174
|
-
projectDir: originalCwd,
|
|
175
|
-
timestamp: new Date(),
|
|
176
|
-
sessionId: context.sessionId ?? "",
|
|
177
|
-
transcriptPath: context.messageManager?.getTranscriptPath?.() ?? "",
|
|
178
|
-
cwd: originalCwd,
|
|
179
|
-
worktreePath,
|
|
180
|
-
env: Object.fromEntries(
|
|
181
|
-
Object.entries(context.sessionEnv ?? process.env).filter(
|
|
182
|
-
(e) => e[1] !== undefined,
|
|
183
|
-
),
|
|
184
|
-
) as Record<string, string>,
|
|
185
|
-
},
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
if (context.messageManager) {
|
|
189
|
-
context.hookManager.processHookResults(
|
|
190
|
-
"WorktreeRemove",
|
|
191
|
-
hookResults,
|
|
192
|
-
context.messageManager,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
hookTriggered = true;
|
|
197
|
-
} catch (error) {
|
|
198
|
-
// Non-blocking: log but don't fail the tool
|
|
199
|
-
logger?.warn("WorktreeRemove hooks execution failed:", error);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
removeWorktree(worktreeInfo);
|
|
204
|
-
|
|
205
|
-
// Clear session state and restore CWD
|
|
206
|
-
const aiManager = context.aiManager;
|
|
207
|
-
if (aiManager) {
|
|
208
|
-
aiManager.setWorktreeSession(null);
|
|
209
|
-
aiManager.setWorkdir(originalCwd);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const discardParts: string[] = [];
|
|
213
|
-
if (summary.commits > 0) {
|
|
214
|
-
discardParts.push(
|
|
215
|
-
`${summary.commits} ${summary.commits === 1 ? "commit" : "commits"}`,
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
if (summary.changedFiles > 0) {
|
|
219
|
-
discardParts.push(
|
|
220
|
-
`${summary.changedFiles} uncommitted ${summary.changedFiles === 1 ? "file" : "files"}`,
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
const discardNote =
|
|
224
|
-
discardParts.length > 0
|
|
225
|
-
? ` Discarded ${discardParts.join(" and ")}.`
|
|
226
|
-
: "";
|
|
227
|
-
const hookNote = hookTriggered
|
|
228
|
-
? " WorktreeRemove hooks were executed."
|
|
229
|
-
: "";
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
success: true,
|
|
233
|
-
content: `Exited and removed worktree at ${worktreePath}.${discardNote} Session is now back in ${originalCwd}.${hookNote}`,
|
|
234
|
-
};
|
|
235
|
-
},
|
|
236
|
-
};
|