wave-agent-sdk 0.0.15 → 0.0.17-alpha.0

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 (204) hide show
  1. package/dist/agent.d.ts +11 -1
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +71 -11
  4. package/dist/constants/prompts.d.ts +14 -0
  5. package/dist/constants/prompts.d.ts.map +1 -0
  6. package/dist/constants/prompts.js +61 -0
  7. package/dist/constants/tools.d.ts +18 -0
  8. package/dist/constants/tools.d.ts.map +1 -0
  9. package/dist/constants/tools.js +17 -0
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +6 -0
  13. package/dist/managers/aiManager.d.ts +8 -2
  14. package/dist/managers/aiManager.d.ts.map +1 -1
  15. package/dist/managers/aiManager.js +52 -16
  16. package/dist/managers/hookManager.d.ts +7 -2
  17. package/dist/managers/hookManager.d.ts.map +1 -1
  18. package/dist/managers/hookManager.js +10 -0
  19. package/dist/managers/liveConfigManager.d.ts +1 -1
  20. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  21. package/dist/managers/liveConfigManager.js +43 -22
  22. package/dist/managers/permissionManager.d.ts +50 -1
  23. package/dist/managers/permissionManager.d.ts.map +1 -1
  24. package/dist/managers/permissionManager.js +289 -58
  25. package/dist/managers/planManager.d.ts +21 -0
  26. package/dist/managers/planManager.d.ts.map +1 -0
  27. package/dist/managers/planManager.js +35 -0
  28. package/dist/managers/pluginManager.d.ts +57 -0
  29. package/dist/managers/pluginManager.d.ts.map +1 -0
  30. package/dist/managers/pluginManager.js +124 -0
  31. package/dist/managers/pluginScopeManager.d.ts +35 -0
  32. package/dist/managers/pluginScopeManager.d.ts.map +1 -0
  33. package/dist/managers/pluginScopeManager.js +39 -0
  34. package/dist/managers/skillManager.d.ts +4 -0
  35. package/dist/managers/skillManager.d.ts.map +1 -1
  36. package/dist/managers/skillManager.js +15 -0
  37. package/dist/managers/slashCommandManager.d.ts +4 -0
  38. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  39. package/dist/managers/slashCommandManager.js +45 -3
  40. package/dist/managers/subagentManager.d.ts.map +1 -1
  41. package/dist/managers/subagentManager.js +3 -1
  42. package/dist/managers/toolManager.d.ts +4 -0
  43. package/dist/managers/toolManager.d.ts.map +1 -1
  44. package/dist/managers/toolManager.js +24 -1
  45. package/dist/services/GitService.d.ts +16 -0
  46. package/dist/services/GitService.d.ts.map +1 -0
  47. package/dist/services/GitService.js +75 -0
  48. package/dist/services/MarketplaceService.d.ts +62 -0
  49. package/dist/services/MarketplaceService.d.ts.map +1 -0
  50. package/dist/services/MarketplaceService.js +320 -0
  51. package/dist/services/aiService.d.ts +1 -0
  52. package/dist/services/aiService.d.ts.map +1 -1
  53. package/dist/services/aiService.js +11 -11
  54. package/dist/services/configurationService.d.ts +11 -4
  55. package/dist/services/configurationService.d.ts.map +1 -1
  56. package/dist/services/configurationService.js +211 -67
  57. package/dist/services/fileWatcher.js +3 -3
  58. package/dist/services/hook.js +1 -1
  59. package/dist/services/pluginLoader.d.ts +35 -0
  60. package/dist/services/pluginLoader.d.ts.map +1 -0
  61. package/dist/services/pluginLoader.js +149 -0
  62. package/dist/tools/askUserQuestion.d.ts +3 -0
  63. package/dist/tools/askUserQuestion.d.ts.map +1 -0
  64. package/dist/tools/askUserQuestion.js +109 -0
  65. package/dist/tools/bashTool.d.ts.map +1 -1
  66. package/dist/tools/bashTool.js +37 -40
  67. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  68. package/dist/tools/deleteFileTool.js +17 -19
  69. package/dist/tools/editTool.d.ts.map +1 -1
  70. package/dist/tools/editTool.js +29 -36
  71. package/dist/tools/exitPlanMode.d.ts +6 -0
  72. package/dist/tools/exitPlanMode.d.ts.map +1 -0
  73. package/dist/tools/exitPlanMode.js +76 -0
  74. package/dist/tools/globTool.d.ts.map +1 -1
  75. package/dist/tools/globTool.js +4 -3
  76. package/dist/tools/grepTool.d.ts.map +1 -1
  77. package/dist/tools/grepTool.js +5 -4
  78. package/dist/tools/lsTool.d.ts.map +1 -1
  79. package/dist/tools/lsTool.js +16 -3
  80. package/dist/tools/lspTool.d.ts.map +1 -1
  81. package/dist/tools/lspTool.js +3 -2
  82. package/dist/tools/multiEditTool.d.ts.map +1 -1
  83. package/dist/tools/multiEditTool.js +24 -31
  84. package/dist/tools/readTool.d.ts.map +1 -1
  85. package/dist/tools/readTool.js +16 -3
  86. package/dist/tools/skillTool.d.ts.map +1 -1
  87. package/dist/tools/skillTool.js +30 -27
  88. package/dist/tools/taskTool.d.ts.map +1 -1
  89. package/dist/tools/taskTool.js +36 -31
  90. package/dist/tools/todoWriteTool.d.ts.map +1 -1
  91. package/dist/tools/todoWriteTool.js +3 -2
  92. package/dist/tools/writeTool.d.ts.map +1 -1
  93. package/dist/tools/writeTool.js +13 -17
  94. package/dist/types/commands.d.ts +1 -0
  95. package/dist/types/commands.d.ts.map +1 -1
  96. package/dist/types/config.d.ts +2 -0
  97. package/dist/types/config.d.ts.map +1 -1
  98. package/dist/types/configuration.d.ts +36 -1
  99. package/dist/types/configuration.d.ts.map +1 -1
  100. package/dist/types/hooks.d.ts +2 -15
  101. package/dist/types/hooks.d.ts.map +1 -1
  102. package/dist/types/index.d.ts +3 -0
  103. package/dist/types/index.d.ts.map +1 -1
  104. package/dist/types/index.js +3 -0
  105. package/dist/types/marketplace.d.ts +43 -0
  106. package/dist/types/marketplace.d.ts.map +1 -0
  107. package/dist/types/marketplace.js +1 -0
  108. package/dist/types/permissions.d.ts +4 -2
  109. package/dist/types/permissions.d.ts.map +1 -1
  110. package/dist/types/permissions.js +8 -5
  111. package/dist/types/plugins.d.ts +35 -0
  112. package/dist/types/plugins.d.ts.map +1 -0
  113. package/dist/types/plugins.js +1 -0
  114. package/dist/types/tools.d.ts +17 -0
  115. package/dist/types/tools.d.ts.map +1 -1
  116. package/dist/utils/bashHistory.d.ts +3 -3
  117. package/dist/utils/bashHistory.d.ts.map +1 -1
  118. package/dist/utils/bashHistory.js +10 -8
  119. package/dist/utils/bashParser.d.ts.map +1 -1
  120. package/dist/utils/bashParser.js +63 -20
  121. package/dist/utils/configPaths.d.ts +4 -0
  122. package/dist/utils/configPaths.d.ts.map +1 -1
  123. package/dist/utils/configPaths.js +6 -0
  124. package/dist/utils/constants.d.ts +4 -0
  125. package/dist/utils/constants.d.ts.map +1 -1
  126. package/dist/utils/constants.js +4 -0
  127. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  128. package/dist/utils/convertMessagesForAPI.js +4 -2
  129. package/dist/utils/customCommands.d.ts +6 -0
  130. package/dist/utils/customCommands.d.ts.map +1 -1
  131. package/dist/utils/customCommands.js +1 -1
  132. package/dist/utils/editUtils.d.ts +17 -0
  133. package/dist/utils/editUtils.d.ts.map +1 -0
  134. package/dist/utils/editUtils.js +69 -0
  135. package/dist/utils/markdownParser.d.ts.map +1 -1
  136. package/dist/utils/markdownParser.js +25 -1
  137. package/dist/utils/messageOperations.d.ts.map +1 -1
  138. package/dist/utils/messageOperations.js +2 -1
  139. package/dist/utils/nameGenerator.d.ts +8 -0
  140. package/dist/utils/nameGenerator.d.ts.map +1 -0
  141. package/dist/utils/nameGenerator.js +75 -0
  142. package/dist/utils/openaiClient.d.ts.map +1 -1
  143. package/dist/utils/openaiClient.js +14 -2
  144. package/dist/utils/pathSafety.d.ts.map +1 -1
  145. package/dist/utils/pathSafety.js +17 -2
  146. package/package.json +6 -1
  147. package/src/agent.ts +95 -10
  148. package/src/constants/prompts.ts +93 -0
  149. package/src/constants/tools.ts +17 -0
  150. package/src/index.ts +8 -0
  151. package/src/managers/aiManager.ts +86 -18
  152. package/src/managers/hookManager.ts +23 -6
  153. package/src/managers/liveConfigManager.ts +56 -29
  154. package/src/managers/permissionManager.ts +361 -61
  155. package/src/managers/planManager.ts +45 -0
  156. package/src/managers/pluginManager.ts +182 -0
  157. package/src/managers/pluginScopeManager.ts +75 -0
  158. package/src/managers/skillManager.ts +18 -0
  159. package/src/managers/slashCommandManager.ts +67 -5
  160. package/src/managers/subagentManager.ts +3 -1
  161. package/src/managers/toolManager.ts +25 -3
  162. package/src/services/GitService.ts +97 -0
  163. package/src/services/MarketplaceService.ts +428 -0
  164. package/src/services/aiService.ts +33 -16
  165. package/src/services/configurationService.ts +244 -86
  166. package/src/services/fileWatcher.ts +3 -3
  167. package/src/services/hook.ts +1 -1
  168. package/src/services/pluginLoader.ts +181 -0
  169. package/src/tools/askUserQuestion.ts +128 -0
  170. package/src/tools/bashTool.ts +52 -49
  171. package/src/tools/deleteFileTool.ts +22 -28
  172. package/src/tools/editTool.ts +42 -48
  173. package/src/tools/exitPlanMode.ts +93 -0
  174. package/src/tools/globTool.ts +4 -4
  175. package/src/tools/grepTool.ts +9 -5
  176. package/src/tools/lsTool.ts +27 -4
  177. package/src/tools/lspTool.ts +3 -2
  178. package/src/tools/multiEditTool.ts +40 -42
  179. package/src/tools/readTool.ts +23 -4
  180. package/src/tools/skillTool.ts +35 -30
  181. package/src/tools/taskTool.ts +39 -33
  182. package/src/tools/todoWriteTool.ts +3 -2
  183. package/src/tools/writeTool.ts +19 -27
  184. package/src/types/commands.ts +1 -0
  185. package/src/types/config.ts +2 -0
  186. package/src/types/configuration.ts +42 -1
  187. package/src/types/hooks.ts +8 -25
  188. package/src/types/index.ts +3 -0
  189. package/src/types/marketplace.ts +52 -0
  190. package/src/types/permissions.ts +29 -6
  191. package/src/types/plugins.ts +37 -0
  192. package/src/types/tools.ts +20 -0
  193. package/src/utils/bashHistory.ts +9 -16
  194. package/src/utils/bashParser.ts +97 -46
  195. package/src/utils/configPaths.ts +7 -0
  196. package/src/utils/constants.ts +5 -0
  197. package/src/utils/convertMessagesForAPI.ts +4 -2
  198. package/src/utils/customCommands.ts +1 -1
  199. package/src/utils/editUtils.ts +82 -0
  200. package/src/utils/markdownParser.ts +28 -1
  201. package/src/utils/messageOperations.ts +2 -1
  202. package/src/utils/nameGenerator.ts +78 -0
  203. package/src/utils/openaiClient.ts +14 -2
  204. package/src/utils/pathSafety.ts +16 -2
@@ -0,0 +1,109 @@
1
+ import { ASK_USER_QUESTION_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, } from "../constants/tools.js";
2
+ export const askUserQuestionTool = {
3
+ name: ASK_USER_QUESTION_TOOL_NAME,
4
+ config: {
5
+ type: "function",
6
+ function: {
7
+ name: ASK_USER_QUESTION_TOOL_NAME,
8
+ description: `Asks the user multiple choice questions to gather information, clarify ambiguity, understand preferences, make decisions or offer them choices.
9
+ Use this tool when you need to ask the user questions during execution. This allows you to:
10
+ 1. Gather user preferences or requirements
11
+ 2. Clarify ambiguous instructions
12
+ 3. Get decisions on implementation choices as you work
13
+ 4. Offer choices to the user about what direction to take.
14
+
15
+ Usage notes:
16
+ - Users will always be able to select "Other" to provide custom text input
17
+ - Use multiSelect: true to allow multiple answers to be selected for a question
18
+ - If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
19
+
20
+ Plan mode note: In plan mode, use this tool to clarify requirements or choose between approaches BEFORE finalizing your plan. Do NOT use this tool to ask "Is my plan ready?" or "Should I proceed?" - use ${EXIT_PLAN_MODE_TOOL_NAME} for plan approval.`,
21
+ parameters: {
22
+ type: "object",
23
+ properties: {
24
+ questions: {
25
+ type: "array",
26
+ minItems: 1,
27
+ maxItems: 4,
28
+ items: {
29
+ type: "object",
30
+ properties: {
31
+ question: {
32
+ type: "string",
33
+ description: "The complete question to ask the user.",
34
+ },
35
+ header: {
36
+ type: "string",
37
+ maxLength: 12,
38
+ description: "Very short label displayed as a chip/tag (max 12 chars).",
39
+ },
40
+ options: {
41
+ type: "array",
42
+ minItems: 2,
43
+ maxItems: 4,
44
+ items: {
45
+ type: "object",
46
+ properties: {
47
+ label: {
48
+ type: "string",
49
+ description: "The display text for this option.",
50
+ },
51
+ description: {
52
+ type: "string",
53
+ description: "Explanation of what this option means.",
54
+ },
55
+ isRecommended: {
56
+ type: "boolean",
57
+ description: "Whether this option is recommended.",
58
+ },
59
+ },
60
+ required: ["label"],
61
+ },
62
+ },
63
+ multiSelect: {
64
+ type: "boolean",
65
+ default: false,
66
+ description: "Allow multiple answers to be selected.",
67
+ },
68
+ },
69
+ required: ["question", "header", "options"],
70
+ },
71
+ },
72
+ },
73
+ required: ["questions"],
74
+ },
75
+ },
76
+ },
77
+ execute: async (args, context) => {
78
+ const { questions } = args;
79
+ if (!context.permissionManager) {
80
+ throw new Error(`Permission manager is required for ${ASK_USER_QUESTION_TOOL_NAME} tool`);
81
+ }
82
+ const permissionContext = context.permissionManager.createContext(ASK_USER_QUESTION_TOOL_NAME, context.permissionMode || "default", context.canUseToolCallback, { questions });
83
+ permissionContext.hidePersistentOption = true; // Always hide persistent option for questions
84
+ const decision = await context.permissionManager.checkPermission(permissionContext);
85
+ if (decision.behavior === "deny") {
86
+ return {
87
+ success: false,
88
+ content: "",
89
+ error: decision.message || "User declined to answer questions",
90
+ };
91
+ }
92
+ // The answers are expected to be returned in the decision message or a specialized field
93
+ // For now, we assume the UI returns the answers as a JSON string in the message
94
+ try {
95
+ const answers = JSON.parse(decision.message || "{}");
96
+ return {
97
+ success: true,
98
+ content: JSON.stringify({ answers }),
99
+ };
100
+ }
101
+ catch {
102
+ return {
103
+ success: false,
104
+ content: "",
105
+ error: "Failed to parse user answers",
106
+ };
107
+ }
108
+ },
109
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"bashTool.d.ts","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAKtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAiUtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,UA+F5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,UA8E1B,CAAC"}
1
+ {"version":3,"file":"bashTool.d.ts","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAetE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA0TtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,UA+F5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,UA8E1B,CAAC"}
@@ -1,17 +1,18 @@
1
1
  import { spawn } from "child_process";
2
2
  import { logger } from "../utils/globalLogger.js";
3
3
  import { stripAnsiColors } from "../utils/stringUtils.js";
4
+ import { BASH_TOOL_NAME, BASH_OUTPUT_TOOL_NAME, KILL_BASH_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME, READ_TOOL_NAME, EDIT_TOOL_NAME, WRITE_TOOL_NAME, } from "../constants/tools.js";
4
5
  const MAX_OUTPUT_LENGTH = 30000;
5
6
  const BASH_DEFAULT_TIMEOUT_MS = 120000;
6
7
  /**
7
8
  * Bash command execution tool - supports both foreground and background execution
8
9
  */
9
10
  export const bashTool = {
10
- name: "Bash",
11
+ name: BASH_TOOL_NAME,
11
12
  config: {
12
13
  type: "function",
13
14
  function: {
14
- name: "Bash",
15
+ name: BASH_TOOL_NAME,
15
16
  description: `Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
16
17
 
17
18
  IMPORTANT: This tool is for terminal operations like git, npm, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead.
@@ -37,17 +38,17 @@ Usage notes:
37
38
  - You can specify an optional timeout in milliseconds (up to ${BASH_DEFAULT_TIMEOUT_MS}ms / ${BASH_DEFAULT_TIMEOUT_MS / 60000} minutes). If not specified, commands will timeout after ${BASH_DEFAULT_TIMEOUT_MS}ms (${BASH_DEFAULT_TIMEOUT_MS / 60000} minutes).
38
39
  - It is very helpful if you write a clear, concise description of what this command does in 5-10 words.
39
40
  - If the output exceeds ${MAX_OUTPUT_LENGTH} characters, output will be truncated before being returned to you.
40
- - You can use the \`run_in_background\` parameter to run the command in the background, which allows you to continue working while the command runs. You can monitor the output using the Bash tool as it becomes available. You do not need to use '&' at the end of the command when using this parameter.
41
- - Avoid using Bash with the \`find\`, \`grep\`, \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or \`echo\` commands, unless explicitly instructed or when these commands are truly necessary for the task. Instead, always prefer using the dedicated tools for these commands:
42
- - File search: Use Glob (NOT find or ls)
43
- - Content search: Use Grep (NOT grep or rg)
44
- - Read files: Use Read (NOT cat/head/tail)
45
- - Edit files: Use Edit (NOT sed/awk)
46
- - Write files: Use Write (NOT echo >/cat <<EOF)
41
+ - You can use the \`run_in_background\` parameter to run the command in the background, which allows you to continue working while the command runs. You can monitor the output using the ${BASH_TOOL_NAME} tool as it becomes available. You do not need to use '&' at the end of the command when using this parameter.
42
+ - Avoid using ${BASH_TOOL_NAME} with the \`find\`, \`grep\`, \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or \`echo\` commands, unless explicitly instructed or when these commands are truly necessary for the task. Instead, always prefer using the dedicated tools for these commands:
43
+ - File search: Use ${GLOB_TOOL_NAME} (NOT find or ls)
44
+ - Content search: Use ${GREP_TOOL_NAME} (NOT grep or rg)
45
+ - Read files: Use ${READ_TOOL_NAME} (NOT cat/head/tail)
46
+ - Edit files: Use ${EDIT_TOOL_NAME} (NOT sed/awk)
47
+ - Write files: Use ${WRITE_TOOL_NAME} (NOT echo >/cat <<EOF)
47
48
  - Communication: Output text directly (NOT echo/printf)
48
49
  - When issuing multiple commands:
49
- - If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. For example, if you need to run "git status" and "git diff", send a single message with two Bash tool calls in parallel.
50
- - If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together (e.g., \`git add . && git commit -m "message" && git push\`). For instance, if one operation must complete before another starts (like mkdir before cp, Write before Bash for git operations, or git add before git commit), run these operations sequentially instead.
50
+ - If the commands are independent and can run in parallel, make multiple ${BASH_TOOL_NAME} tool calls in a single message. For example, if you need to run "git status" and "git diff", send a single message with two ${BASH_TOOL_NAME} tool calls in parallel.
51
+ - If the commands depend on each other and must run sequentially, use a single ${BASH_TOOL_NAME} call with '&&' to chain them together (e.g., \`git add . && git commit -m "message" && git push\`). For instance, if one operation must complete before another starts (like mkdir before cp, ${WRITE_TOOL_NAME} before ${BASH_TOOL_NAME} for git operations, or git add before git commit), run these operations sequentially instead.
51
52
  - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail
52
53
  - DO NOT use newlines to separate commands (newlines are ok in quoted strings)
53
54
  - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \`cd\`. You may use \`cd\` if the User explicitly requests it.
@@ -75,7 +76,7 @@ Usage notes:
75
76
  },
76
77
  run_in_background: {
77
78
  type: "boolean",
78
- description: "Set to true to run this command in the background. Use BashOutput to read the output later.",
79
+ description: `Set to true to run this command in the background. Use ${BASH_OUTPUT_TOOL_NAME} to read the output later.`,
79
80
  },
80
81
  },
81
82
  required: ["command"],
@@ -106,35 +107,31 @@ Usage notes:
106
107
  };
107
108
  }
108
109
  // Permission check after validation but before real operation
109
- if (context.permissionManager &&
110
- context.permissionMode &&
111
- context.permissionMode !== "bypassPermissions") {
112
- if (context.permissionManager.isRestrictedTool("Bash")) {
113
- try {
114
- const permissionContext = context.permissionManager.createContext("Bash", context.permissionMode, context.canUseToolCallback, {
115
- command,
116
- description,
117
- run_in_background: runInBackground,
118
- timeout,
119
- workdir: context.workdir,
120
- });
121
- const permissionResult = await context.permissionManager.checkPermission(permissionContext);
122
- if (permissionResult.behavior === "deny") {
123
- return {
124
- success: false,
125
- content: "",
126
- error: `Bash operation denied by user, reason: ${permissionResult.message || "No reason provided"}`,
127
- };
128
- }
129
- }
130
- catch {
110
+ if (context.permissionManager) {
111
+ try {
112
+ const permissionContext = context.permissionManager.createContext(BASH_TOOL_NAME, context.permissionMode || "default", context.canUseToolCallback, {
113
+ command,
114
+ description,
115
+ run_in_background: runInBackground,
116
+ timeout,
117
+ workdir: context.workdir,
118
+ });
119
+ const permissionResult = await context.permissionManager.checkPermission(permissionContext);
120
+ if (permissionResult.behavior === "deny") {
131
121
  return {
132
122
  success: false,
133
123
  content: "",
134
- error: "Permission check failed",
124
+ error: `${BASH_TOOL_NAME} operation denied, reason: ${permissionResult.message || "No reason provided"}`,
135
125
  };
136
126
  }
137
127
  }
128
+ catch {
129
+ return {
130
+ success: false,
131
+ content: "",
132
+ error: "Permission check failed",
133
+ };
134
+ }
138
135
  }
139
136
  if (runInBackground) {
140
137
  // Background execution
@@ -149,7 +146,7 @@ Usage notes:
149
146
  const shellId = backgroundBashManager.startShell(command, timeout);
150
147
  return {
151
148
  success: true,
152
- content: `Command started in background with ID: ${shellId}. Use BashOutput tool with bash_id="${shellId}" to monitor output.`,
149
+ content: `Command started in background with ID: ${shellId}. Use ${BASH_OUTPUT_TOOL_NAME} tool with bash_id="${shellId}" to monitor output.`,
153
150
  shortResult: `Background process ${shellId} started`,
154
151
  };
155
152
  }
@@ -292,11 +289,11 @@ Usage notes:
292
289
  * BashOutput tool - retrieves output from background bash shells
293
290
  */
294
291
  export const bashOutputTool = {
295
- name: "BashOutput",
292
+ name: BASH_OUTPUT_TOOL_NAME,
296
293
  config: {
297
294
  type: "function",
298
295
  function: {
299
- name: "BashOutput",
296
+ name: BASH_OUTPUT_TOOL_NAME,
300
297
  description: "Retrieves output from a running or completed background bash shell",
301
298
  parameters: {
302
299
  type: "object",
@@ -377,11 +374,11 @@ export const bashOutputTool = {
377
374
  * KillBash tool - kills a running background bash shell
378
375
  */
379
376
  export const killBashTool = {
380
- name: "KillBash",
377
+ name: KILL_BASH_TOOL_NAME,
381
378
  config: {
382
379
  type: "function",
383
380
  function: {
384
- name: "KillBash",
381
+ name: KILL_BASH_TOOL_NAME,
385
382
  description: "Kills a running background bash shell by its ID",
386
383
  parameters: {
387
384
  type: "object",
@@ -1 +1 @@
1
- {"version":3,"file":"deleteFileTool.d.ts","sourceRoot":"","sources":["../../src/tools/deleteFileTool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAGtE;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,UA6G5B,CAAC"}
1
+ {"version":3,"file":"deleteFileTool.d.ts","sourceRoot":"","sources":["../../src/tools/deleteFileTool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAItE;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,UAsG5B,CAAC"}
@@ -1,15 +1,16 @@
1
1
  import { unlink } from "fs/promises";
2
2
  import { logger } from "../utils/globalLogger.js";
3
3
  import { resolvePath, getDisplayPath } from "../utils/path.js";
4
+ import { DELETE_FILE_TOOL_NAME } from "../constants/tools.js";
4
5
  /**
5
6
  * Delete file tool plugin
6
7
  */
7
8
  export const deleteFileTool = {
8
- name: "Delete",
9
+ name: DELETE_FILE_TOOL_NAME,
9
10
  config: {
10
11
  type: "function",
11
12
  function: {
12
- name: "Delete",
13
+ name: DELETE_FILE_TOOL_NAME,
13
14
  description: `Deletes a file at the specified path. The operation will fail gracefully if:
14
15
  - The file doesn't exist
15
16
  - The operation is rejected for security reasons
@@ -38,29 +39,26 @@ export const deleteFileTool = {
38
39
  try {
39
40
  const filePath = resolvePath(targetFile, context.workdir);
40
41
  // Permission check after validation but before real operation
41
- if (context.permissionManager &&
42
- context.permissionMode &&
43
- context.permissionMode !== "bypassPermissions") {
44
- if (context.permissionManager.isRestrictedTool("Delete")) {
45
- try {
46
- const permissionContext = context.permissionManager.createContext("Delete", context.permissionMode, context.canUseToolCallback, { target_file: targetFile });
47
- const permissionResult = await context.permissionManager.checkPermission(permissionContext);
48
- if (permissionResult.behavior === "deny") {
49
- return {
50
- success: false,
51
- content: "",
52
- error: `Delete operation denied by user, reason: ${permissionResult.message || "No reason provided"}`,
53
- };
54
- }
55
- }
56
- catch {
42
+ // Permission check after validation but before real operation
43
+ if (context.permissionManager) {
44
+ try {
45
+ const permissionContext = context.permissionManager.createContext(DELETE_FILE_TOOL_NAME, context.permissionMode || "default", context.canUseToolCallback, { target_file: targetFile });
46
+ const permissionResult = await context.permissionManager.checkPermission(permissionContext);
47
+ if (permissionResult.behavior === "deny") {
57
48
  return {
58
49
  success: false,
59
50
  content: "",
60
- error: "Permission check failed",
51
+ error: `${DELETE_FILE_TOOL_NAME} operation denied, reason: ${permissionResult.message || "No reason provided"}`,
61
52
  };
62
53
  }
63
54
  }
55
+ catch {
56
+ return {
57
+ success: false,
58
+ content: "",
59
+ error: "Permission check failed",
60
+ };
61
+ }
64
62
  }
65
63
  // Delete file
66
64
  await unlink(filePath);
@@ -1 +1 @@
1
- {"version":3,"file":"editTool.d.ts","sourceRoot":"","sources":["../../src/tools/editTool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AActE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAwMtB,CAAC"}
1
+ {"version":3,"file":"editTool.d.ts","sourceRoot":"","sources":["../../src/tools/editTool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAmBtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAoMtB,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import { readFile, writeFile } from "fs/promises";
2
2
  import { logger } from "../utils/globalLogger.js";
3
3
  import { resolvePath, getDisplayPath } from "../utils/path.js";
4
+ import { findIndentationInsensitiveMatch, escapeRegExp, } from "../utils/editUtils.js";
5
+ import { EDIT_TOOL_NAME, READ_TOOL_NAME } from "../constants/tools.js";
4
6
  /**
5
7
  * Format compact parameter display
6
8
  */
@@ -12,13 +14,13 @@ function formatCompactParams(args, context) {
12
14
  * Single file edit tool plugin
13
15
  */
14
16
  export const editTool = {
15
- name: "Edit",
17
+ name: EDIT_TOOL_NAME,
16
18
  formatCompactParams,
17
19
  config: {
18
20
  type: "function",
19
21
  function: {
20
- name: "Edit",
21
- description: "Performs exact string replacements in files. \n\nUsage:\n- You must use your `Read` tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file. \n- When editing text from read_file tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.\n- The edit will FAIL if `old_string` is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use `replace_all` to change every instance of `old_string`. \n- Use `replace_all` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.",
22
+ name: EDIT_TOOL_NAME,
23
+ description: `Performs exact string replacements in files. \n\nUsage:\n- You must use your \`${READ_TOOL_NAME}\` tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file. \n- When editing text from read_file tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.\n- The edit will FAIL if \`old_string\` is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use \`replace_all\` to change every instance of \`old_string\`. \n- Use \`replace_all\` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.`,
22
24
  parameters: {
23
25
  type: "object",
24
26
  properties: {
@@ -93,8 +95,9 @@ export const editTool = {
93
95
  error: `Failed to read file: ${readError instanceof Error ? readError.message : String(readError)}`,
94
96
  };
95
97
  }
96
- // Check if old_string exists
97
- if (!originalContent.includes(oldString)) {
98
+ // Check if old_string exists (with smart indentation matching)
99
+ const matchedOldString = findIndentationInsensitiveMatch(originalContent, oldString);
100
+ if (!matchedOldString) {
98
101
  return {
99
102
  success: false,
100
103
  content: "",
@@ -105,13 +108,13 @@ export const editTool = {
105
108
  let replacementCount;
106
109
  if (replaceAll) {
107
110
  // Replace all matches
108
- const regex = new RegExp(escapeRegExp(oldString), "g");
111
+ const regex = new RegExp(escapeRegExp(matchedOldString), "g");
109
112
  newContent = originalContent.replace(regex, newString);
110
113
  replacementCount = (originalContent.match(regex) || []).length;
111
114
  }
112
115
  else {
113
116
  // Replace only the first match, but first check if it's unique
114
- const matches = originalContent.split(oldString).length - 1;
117
+ const matches = originalContent.split(matchedOldString).length - 1;
115
118
  if (matches > 1) {
116
119
  return {
117
120
  success: false,
@@ -119,38 +122,34 @@ export const editTool = {
119
122
  error: `old_string appears ${matches} times in the file. Either provide a larger string with more surrounding context to make it unique or use replace_all=true to change every instance.`,
120
123
  };
121
124
  }
122
- newContent = originalContent.replace(oldString, newString);
125
+ newContent = originalContent.replace(matchedOldString, newString);
123
126
  replacementCount = 1;
124
127
  }
125
128
  // Permission check after validation but before real operation
126
- if (context.permissionManager &&
127
- context.permissionMode &&
128
- context.permissionMode !== "bypassPermissions") {
129
- if (context.permissionManager.isRestrictedTool("Edit")) {
130
- try {
131
- const permissionContext = context.permissionManager.createContext("Edit", context.permissionMode, context.canUseToolCallback, {
132
- file_path: filePath,
133
- old_string: oldString,
134
- new_string: newString,
135
- replace_all: replaceAll,
136
- });
137
- const permissionResult = await context.permissionManager.checkPermission(permissionContext);
138
- if (permissionResult.behavior === "deny") {
139
- return {
140
- success: false,
141
- content: "",
142
- error: `Edit operation denied by user, reason: ${permissionResult.message || "No reason provided"}`,
143
- };
144
- }
145
- }
146
- catch {
129
+ if (context.permissionManager) {
130
+ try {
131
+ const permissionContext = context.permissionManager.createContext(EDIT_TOOL_NAME, context.permissionMode || "default", context.canUseToolCallback, {
132
+ file_path: filePath,
133
+ old_string: oldString,
134
+ new_string: newString,
135
+ replace_all: replaceAll,
136
+ });
137
+ const permissionResult = await context.permissionManager.checkPermission(permissionContext);
138
+ if (permissionResult.behavior === "deny") {
147
139
  return {
148
140
  success: false,
149
141
  content: "",
150
- error: "Permission check failed",
142
+ error: `${EDIT_TOOL_NAME} operation denied, reason: ${permissionResult.message || "No reason provided"}`,
151
143
  };
152
144
  }
153
145
  }
146
+ catch {
147
+ return {
148
+ success: false,
149
+ content: "",
150
+ error: "Permission check failed",
151
+ };
152
+ }
154
153
  }
155
154
  // Write file
156
155
  try {
@@ -185,9 +184,3 @@ export const editTool = {
185
184
  }
186
185
  },
187
186
  };
188
- /**
189
- * Escape regular expression special characters
190
- */
191
- function escapeRegExp(string) {
192
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
193
- }
@@ -0,0 +1,6 @@
1
+ import type { ToolPlugin } from "./types.js";
2
+ /**
3
+ * Exit Plan Mode Tool Plugin
4
+ */
5
+ export declare const exitPlanModeTool: ToolPlugin;
6
+ //# sourceMappingURL=exitPlanMode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exitPlanMode.d.ts","sourceRoot":"","sources":["../../src/tools/exitPlanMode.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAGtE;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,UAoF9B,CAAC"}
@@ -0,0 +1,76 @@
1
+ import { readFile } from "fs/promises";
2
+ import { logger } from "../utils/globalLogger.js";
3
+ import { EXIT_PLAN_MODE_TOOL_NAME } from "../constants/tools.js";
4
+ /**
5
+ * Exit Plan Mode Tool Plugin
6
+ */
7
+ export const exitPlanModeTool = {
8
+ name: EXIT_PLAN_MODE_TOOL_NAME,
9
+ config: {
10
+ type: "function",
11
+ function: {
12
+ name: EXIT_PLAN_MODE_TOOL_NAME,
13
+ description: "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. This tool will read the plan from the file specified in the system message and present it to the user for confirmation. You should have already written your plan to that file before calling this tool.",
14
+ parameters: {
15
+ type: "object",
16
+ properties: {},
17
+ required: [],
18
+ additionalProperties: false,
19
+ },
20
+ },
21
+ },
22
+ execute: async (_args, context) => {
23
+ try {
24
+ if (!context.permissionManager) {
25
+ return {
26
+ success: false,
27
+ content: "",
28
+ error: "Permission manager is not available",
29
+ };
30
+ }
31
+ const planFilePath = context.permissionManager.getPlanFilePath();
32
+ if (!planFilePath) {
33
+ return {
34
+ success: false,
35
+ content: "",
36
+ error: "Plan file path is not set",
37
+ };
38
+ }
39
+ let planContent = "";
40
+ try {
41
+ planContent = await readFile(planFilePath, "utf-8");
42
+ }
43
+ catch (error) {
44
+ return {
45
+ success: false,
46
+ content: "",
47
+ error: `Failed to read plan file: ${error instanceof Error ? error.message : String(error)}`,
48
+ };
49
+ }
50
+ // Permission check triggers the 3-option UI
51
+ const permissionContext = context.permissionManager.createContext(EXIT_PLAN_MODE_TOOL_NAME, context.permissionMode || "plan", context.canUseToolCallback, { plan_content: planContent });
52
+ const permissionResult = await context.permissionManager.checkPermission(permissionContext);
53
+ if (permissionResult.behavior === "deny") {
54
+ return {
55
+ success: false,
56
+ content: permissionResult.message || "Plan rejected by user",
57
+ error: permissionResult.message ? undefined : "Plan rejected by user",
58
+ };
59
+ }
60
+ return {
61
+ success: true,
62
+ content: "Plan approved. Exiting plan mode.",
63
+ shortResult: "Plan approved",
64
+ };
65
+ }
66
+ catch (error) {
67
+ const errorMessage = error instanceof Error ? error.message : String(error);
68
+ logger.error(`ExitPlanMode tool error: ${errorMessage}`);
69
+ return {
70
+ success: false,
71
+ content: "",
72
+ error: errorMessage,
73
+ };
74
+ }
75
+ },
76
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"globTool.d.ts","sourceRoot":"","sources":["../../src/tools/globTool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAItE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA6HtB,CAAC"}
1
+ {"version":3,"file":"globTool.d.ts","sourceRoot":"","sources":["../../src/tools/globTool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAKtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA4HtB,CAAC"}
@@ -2,16 +2,17 @@ import { glob } from "glob";
2
2
  import { stat } from "fs/promises";
3
3
  import { resolvePath, getDisplayPath } from "../utils/path.js";
4
4
  import { getGlobIgnorePatterns } from "../utils/fileFilter.js";
5
+ import { GLOB_TOOL_NAME, TASK_TOOL_NAME } from "../constants/tools.js";
5
6
  /**
6
7
  * Glob Tool Plugin - Fast file pattern matching
7
8
  */
8
9
  export const globTool = {
9
- name: "Glob",
10
+ name: GLOB_TOOL_NAME,
10
11
  config: {
11
12
  type: "function",
12
13
  function: {
13
- name: "Glob",
14
- description: '- Fast file pattern matching tool that works with any codebase size\n- Supports glob patterns like "**/*.js" or "src/**/*.ts"\n- Returns matching file paths sorted by modification time\n- Use this tool when you need to find files by name patterns\n- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead\n- You have the capability to call multiple tools in a single response. It is always better to speculatively perform multiple searches as a batch that are potentially useful.',
14
+ name: GLOB_TOOL_NAME,
15
+ description: `- Fast file pattern matching tool that works with any codebase size\n- Supports glob patterns like "**/*.js" or "src/**/*.ts"\n- Returns matching file paths sorted by modification time\n- Use this tool when you need to find files by name patterns\n- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the ${TASK_TOOL_NAME} tool instead\n- You have the capability to call multiple tools in a single response. It is always better to speculatively perform multiple searches as a batch that are potentially useful.`,
15
16
  parameters: {
16
17
  type: "object",
17
18
  properties: {
@@ -1 +1 @@
1
- {"version":3,"file":"grepTool.d.ts","sourceRoot":"","sources":["../../src/tools/grepTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAMtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA6QtB,CAAC"}
1
+ {"version":3,"file":"grepTool.d.ts","sourceRoot":"","sources":["../../src/tools/grepTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAWtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA4QtB,CAAC"}
@@ -2,16 +2,17 @@ import { spawn } from "child_process";
2
2
  import { getGlobIgnorePatterns } from "../utils/fileFilter.js";
3
3
  import { rgPath } from "@vscode/ripgrep";
4
4
  import { getDisplayPath } from "../utils/path.js";
5
+ import { GREP_TOOL_NAME, BASH_TOOL_NAME, TASK_TOOL_NAME, } from "../constants/tools.js";
5
6
  /**
6
7
  * Grep tool plugin - powerful search tool based on ripgrep
7
8
  */
8
9
  export const grepTool = {
9
- name: "Grep",
10
+ name: GREP_TOOL_NAME,
10
11
  config: {
11
12
  type: "function",
12
13
  function: {
13
- name: "Grep",
14
- description: 'A powerful search tool built on ripgrep\n\n Usage:\n - ALWAYS use Grep for search tasks. NEVER invoke `grep` or `rg` as a Bash command. The Grep tool has been optimized for correct permissions and access.\n - Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")\n - Filter files with glob parameter (e.g., "*.js", "**/*.tsx") or type parameter (e.g., "js", "py", "rust")\n - Output modes: "content" shows matching lines, "files_with_matches" shows only file paths (default), "count" shows match counts\n - Use Task tool for open-ended searches requiring multiple rounds\n - Pattern syntax: Uses ripgrep (not grep) - literal braces need escaping (use `interface\\{\\}` to find `interface{}` in Go code)\n - Multiline matching: By default patterns match within single lines only. For cross-line patterns like `struct \\{[\\s\\S]*?field`, use `multiline: true`',
14
+ name: GREP_TOOL_NAME,
15
+ description: `A powerful search tool built on ripgrep\n\n Usage:\n - ALWAYS use ${GREP_TOOL_NAME} for search tasks. NEVER invoke \`grep\` or \`rg\` as a ${BASH_TOOL_NAME} command. The ${GREP_TOOL_NAME} tool has been optimized for correct permissions and access.\n - Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")\n - Filter files with glob parameter (e.g., "*.js", "**/*.tsx") or type parameter (e.g., "js", "py", "rust")\n - Output modes: "content" shows matching lines, "files_with_matches" shows only file paths (default), "count" shows match counts\n - Use ${TASK_TOOL_NAME} tool for open-ended searches requiring multiple rounds\n - Pattern syntax: Uses ripgrep (not grep) - literal braces need escaping (use \`interface\\{\\}\` to find \`interface{}\` in Go code)\n - Multiline matching: By default patterns match within single lines only. For cross-line patterns like \`struct \\{[\\s\\S]*?field\`, use \`multiline: true\``,
15
16
  parameters: {
16
17
  type: "object",
17
18
  properties: {
@@ -98,7 +99,7 @@ export const grepTool = {
98
99
  }
99
100
  try {
100
101
  const workdir = context.workdir;
101
- const rgArgs = ["--color=never"];
102
+ const rgArgs = ["--color=never", "--max-columns=500"];
102
103
  // Set output mode
103
104
  if (outputMode === "files_with_matches") {
104
105
  rgArgs.push("-l");
@@ -1 +1 @@
1
- {"version":3,"file":"lsTool.d.ts","sourceRoot":"","sources":["../../src/tools/lsTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAGtE;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,UAiLpB,CAAC"}
1
+ {"version":3,"file":"lsTool.d.ts","sourceRoot":"","sources":["../../src/tools/lsTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAQtE;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,UAmMpB,CAAC"}