@nogataka/smart-edit 0.0.14

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 (186) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +244 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +7 -0
  5. package/dist/devtools/generate_prompt_factory.d.ts +5 -0
  6. package/dist/devtools/generate_prompt_factory.js +114 -0
  7. package/dist/index.d.ts +34 -0
  8. package/dist/index.js +34 -0
  9. package/dist/interprompt/index.d.ts +2 -0
  10. package/dist/interprompt/index.js +1 -0
  11. package/dist/interprompt/jinja_template.d.ts +10 -0
  12. package/dist/interprompt/jinja_template.js +174 -0
  13. package/dist/interprompt/multilang_prompt.d.ts +54 -0
  14. package/dist/interprompt/multilang_prompt.js +302 -0
  15. package/dist/interprompt/prompt_factory.d.ts +16 -0
  16. package/dist/interprompt/prompt_factory.js +189 -0
  17. package/dist/interprompt/util/class_decorators.d.ts +1 -0
  18. package/dist/interprompt/util/class_decorators.js +1 -0
  19. package/dist/interprompt/util/index.d.ts +1 -0
  20. package/dist/interprompt/util/index.js +1 -0
  21. package/dist/serena/agent.d.ts +118 -0
  22. package/dist/serena/agent.js +675 -0
  23. package/dist/serena/agno.d.ts +111 -0
  24. package/dist/serena/agno.js +278 -0
  25. package/dist/serena/analytics.d.ts +24 -0
  26. package/dist/serena/analytics.js +119 -0
  27. package/dist/serena/cli.d.ts +9 -0
  28. package/dist/serena/cli.js +731 -0
  29. package/dist/serena/code_editor.d.ts +42 -0
  30. package/dist/serena/code_editor.js +239 -0
  31. package/dist/serena/config/context_mode.d.ts +41 -0
  32. package/dist/serena/config/context_mode.js +239 -0
  33. package/dist/serena/config/serena_config.d.ts +134 -0
  34. package/dist/serena/config/serena_config.js +718 -0
  35. package/dist/serena/constants.d.ts +18 -0
  36. package/dist/serena/constants.js +27 -0
  37. package/dist/serena/dashboard.d.ts +55 -0
  38. package/dist/serena/dashboard.js +472 -0
  39. package/dist/serena/generated/generated_prompt_factory.d.ts +27 -0
  40. package/dist/serena/generated/generated_prompt_factory.js +42 -0
  41. package/dist/serena/gui_log_viewer.d.ts +41 -0
  42. package/dist/serena/gui_log_viewer.js +436 -0
  43. package/dist/serena/mcp.d.ts +118 -0
  44. package/dist/serena/mcp.js +904 -0
  45. package/dist/serena/project.d.ts +62 -0
  46. package/dist/serena/project.js +321 -0
  47. package/dist/serena/prompt_factory.d.ts +20 -0
  48. package/dist/serena/prompt_factory.js +42 -0
  49. package/dist/serena/resources/config/contexts/agent.yml +8 -0
  50. package/dist/serena/resources/config/contexts/chatgpt.yml +28 -0
  51. package/dist/serena/resources/config/contexts/codex.yml +27 -0
  52. package/dist/serena/resources/config/contexts/context.template.yml +11 -0
  53. package/dist/serena/resources/config/contexts/desktop-app.yml +17 -0
  54. package/dist/serena/resources/config/contexts/ide-assistant.yml +26 -0
  55. package/dist/serena/resources/config/contexts/oaicompat-agent.yml +8 -0
  56. package/dist/serena/resources/config/internal_modes/jetbrains.yml +15 -0
  57. package/dist/serena/resources/config/modes/editing.yml +112 -0
  58. package/dist/serena/resources/config/modes/interactive.yml +11 -0
  59. package/dist/serena/resources/config/modes/mode.template.yml +7 -0
  60. package/dist/serena/resources/config/modes/no-onboarding.yml +8 -0
  61. package/dist/serena/resources/config/modes/onboarding.yml +16 -0
  62. package/dist/serena/resources/config/modes/one-shot.yml +15 -0
  63. package/dist/serena/resources/config/modes/planning.yml +15 -0
  64. package/dist/serena/resources/config/prompt_templates/simple_tool_outputs.yml +75 -0
  65. package/dist/serena/resources/config/prompt_templates/system_prompt.yml +66 -0
  66. package/dist/serena/resources/dashboard/dashboard.js +815 -0
  67. package/dist/serena/resources/dashboard/index.html +314 -0
  68. package/dist/serena/resources/dashboard/jquery.min.js +3 -0
  69. package/dist/serena/resources/dashboard/serena-icon-16.png +0 -0
  70. package/dist/serena/resources/dashboard/serena-icon-32.png +0 -0
  71. package/dist/serena/resources/dashboard/serena-icon-48.png +0 -0
  72. package/dist/serena/resources/dashboard/serena-logs-dark-mode.png +0 -0
  73. package/dist/serena/resources/dashboard/serena-logs.png +0 -0
  74. package/dist/serena/resources/project.template.yml +67 -0
  75. package/dist/serena/resources/serena_config.template.yml +85 -0
  76. package/dist/serena/symbol.d.ts +199 -0
  77. package/dist/serena/symbol.js +616 -0
  78. package/dist/serena/text_utils.d.ts +51 -0
  79. package/dist/serena/text_utils.js +267 -0
  80. package/dist/serena/tools/cmd_tools.d.ts +31 -0
  81. package/dist/serena/tools/cmd_tools.js +48 -0
  82. package/dist/serena/tools/config_tools.d.ts +53 -0
  83. package/dist/serena/tools/config_tools.js +176 -0
  84. package/dist/serena/tools/file_tools.d.ts +231 -0
  85. package/dist/serena/tools/file_tools.js +511 -0
  86. package/dist/serena/tools/index.d.ts +7 -0
  87. package/dist/serena/tools/index.js +7 -0
  88. package/dist/serena/tools/memory_tools.d.ts +60 -0
  89. package/dist/serena/tools/memory_tools.js +135 -0
  90. package/dist/serena/tools/symbol_tools.d.ts +165 -0
  91. package/dist/serena/tools/symbol_tools.js +362 -0
  92. package/dist/serena/tools/tools_base.d.ts +162 -0
  93. package/dist/serena/tools/tools_base.js +378 -0
  94. package/dist/serena/tools/workflow_tools.d.ts +35 -0
  95. package/dist/serena/tools/workflow_tools.js +161 -0
  96. package/dist/serena/util/class_decorators.d.ts +7 -0
  97. package/dist/serena/util/class_decorators.js +37 -0
  98. package/dist/serena/util/exception.d.ts +8 -0
  99. package/dist/serena/util/exception.js +53 -0
  100. package/dist/serena/util/file_system.d.ts +30 -0
  101. package/dist/serena/util/file_system.js +352 -0
  102. package/dist/serena/util/general.d.ts +11 -0
  103. package/dist/serena/util/general.js +42 -0
  104. package/dist/serena/util/git.d.ts +11 -0
  105. package/dist/serena/util/git.js +37 -0
  106. package/dist/serena/util/inspection.d.ts +45 -0
  107. package/dist/serena/util/inspection.js +221 -0
  108. package/dist/serena/util/logging.d.ts +46 -0
  109. package/dist/serena/util/logging.js +205 -0
  110. package/dist/serena/util/shell.d.ts +21 -0
  111. package/dist/serena/util/shell.js +95 -0
  112. package/dist/serena/util/thread.d.ts +23 -0
  113. package/dist/serena/util/thread.js +88 -0
  114. package/dist/serena/version.d.ts +1 -0
  115. package/dist/serena/version.js +23 -0
  116. package/dist/solidlsp/language_servers/autoload.d.ts +23 -0
  117. package/dist/solidlsp/language_servers/autoload.js +25 -0
  118. package/dist/solidlsp/language_servers/bash_language_server.d.ts +10 -0
  119. package/dist/solidlsp/language_servers/bash_language_server.js +64 -0
  120. package/dist/solidlsp/language_servers/clangd_language_server.d.ts +13 -0
  121. package/dist/solidlsp/language_servers/clangd_language_server.js +110 -0
  122. package/dist/solidlsp/language_servers/clojure_lsp.d.ts +13 -0
  123. package/dist/solidlsp/language_servers/clojure_lsp.js +137 -0
  124. package/dist/solidlsp/language_servers/common.d.ts +41 -0
  125. package/dist/solidlsp/language_servers/common.js +365 -0
  126. package/dist/solidlsp/language_servers/csharp_language_server.d.ts +21 -0
  127. package/dist/solidlsp/language_servers/csharp_language_server.js +694 -0
  128. package/dist/solidlsp/language_servers/dart_language_server.d.ts +10 -0
  129. package/dist/solidlsp/language_servers/dart_language_server.js +122 -0
  130. package/dist/solidlsp/language_servers/eclipse_jdtls.d.ts +24 -0
  131. package/dist/solidlsp/language_servers/eclipse_jdtls.js +671 -0
  132. package/dist/solidlsp/language_servers/erlang_language_server.d.ts +22 -0
  133. package/dist/solidlsp/language_servers/erlang_language_server.js +327 -0
  134. package/dist/solidlsp/language_servers/gopls.d.ts +12 -0
  135. package/dist/solidlsp/language_servers/gopls.js +59 -0
  136. package/dist/solidlsp/language_servers/intelephense.d.ts +13 -0
  137. package/dist/solidlsp/language_servers/intelephense.js +121 -0
  138. package/dist/solidlsp/language_servers/jedi_server.d.ts +18 -0
  139. package/dist/solidlsp/language_servers/jedi_server.js +234 -0
  140. package/dist/solidlsp/language_servers/kotlin_language_server.d.ts +19 -0
  141. package/dist/solidlsp/language_servers/kotlin_language_server.js +474 -0
  142. package/dist/solidlsp/language_servers/lua_ls.d.ts +18 -0
  143. package/dist/solidlsp/language_servers/lua_ls.js +319 -0
  144. package/dist/solidlsp/language_servers/nixd_language_server.d.ts +17 -0
  145. package/dist/solidlsp/language_servers/nixd_language_server.js +341 -0
  146. package/dist/solidlsp/language_servers/pyright_server.d.ts +19 -0
  147. package/dist/solidlsp/language_servers/pyright_server.js +180 -0
  148. package/dist/solidlsp/language_servers/r_language_server.d.ts +19 -0
  149. package/dist/solidlsp/language_servers/r_language_server.js +184 -0
  150. package/dist/solidlsp/language_servers/ruby_common.d.ts +10 -0
  151. package/dist/solidlsp/language_servers/ruby_common.js +136 -0
  152. package/dist/solidlsp/language_servers/ruby_lsp.d.ts +18 -0
  153. package/dist/solidlsp/language_servers/ruby_lsp.js +230 -0
  154. package/dist/solidlsp/language_servers/rust_analyzer.d.ts +13 -0
  155. package/dist/solidlsp/language_servers/rust_analyzer.js +96 -0
  156. package/dist/solidlsp/language_servers/solargraph.d.ts +18 -0
  157. package/dist/solidlsp/language_servers/solargraph.js +208 -0
  158. package/dist/solidlsp/language_servers/sourcekit_lsp.d.ts +24 -0
  159. package/dist/solidlsp/language_servers/sourcekit_lsp.js +449 -0
  160. package/dist/solidlsp/language_servers/terraform_ls.d.ts +13 -0
  161. package/dist/solidlsp/language_servers/terraform_ls.js +139 -0
  162. package/dist/solidlsp/language_servers/typescript_language_server.d.ts +20 -0
  163. package/dist/solidlsp/language_servers/typescript_language_server.js +237 -0
  164. package/dist/solidlsp/language_servers/vts_language_server.d.ts +13 -0
  165. package/dist/solidlsp/language_servers/vts_language_server.js +121 -0
  166. package/dist/solidlsp/language_servers/zls.d.ts +20 -0
  167. package/dist/solidlsp/language_servers/zls.js +254 -0
  168. package/dist/solidlsp/ls.d.ts +197 -0
  169. package/dist/solidlsp/ls.js +507 -0
  170. package/dist/solidlsp/ls_config.d.ts +43 -0
  171. package/dist/solidlsp/ls_config.js +157 -0
  172. package/dist/solidlsp/ls_exceptions.d.ts +5 -0
  173. package/dist/solidlsp/ls_exceptions.js +14 -0
  174. package/dist/solidlsp/ls_handler.d.ts +54 -0
  175. package/dist/solidlsp/ls_handler.js +406 -0
  176. package/dist/solidlsp/ls_request.d.ts +31 -0
  177. package/dist/solidlsp/ls_request.js +42 -0
  178. package/dist/solidlsp/ls_types.d.ts +7 -0
  179. package/dist/solidlsp/ls_types.js +8 -0
  180. package/dist/solidlsp/lsp_protocol_handler/server.d.ts +61 -0
  181. package/dist/solidlsp/lsp_protocol_handler/server.js +68 -0
  182. package/dist/solidlsp/util/subprocess_util.d.ts +6 -0
  183. package/dist/solidlsp/util/subprocess_util.js +11 -0
  184. package/dist/solidlsp/util/zip.d.ts +25 -0
  185. package/dist/solidlsp/util/zip.js +188 -0
  186. package/package.json +65 -0
@@ -0,0 +1,511 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { z } from 'zod';
4
+ import { searchFiles } from '../text_utils.js';
5
+ import { scanDirectory } from '../util/file_system.js';
6
+ import { EditedFileContext, SUCCESS_RESULT, Tool, ToolMarkerCanEdit, ToolMarkerOptional, assertIsBufferEncoding } from './tools_base.js';
7
+ export { SUCCESS_RESULT } from './tools_base.js';
8
+ function resolveProjectEncoding(project) {
9
+ const encodingValue = project.projectConfig.encoding ?? 'utf-8';
10
+ assertIsBufferEncoding(encodingValue);
11
+ return encodingValue;
12
+ }
13
+ export class ReadFileTool extends Tool {
14
+ static description = 'Reads a file within the project directory.';
15
+ static inputSchema = z.object({
16
+ relative_path: z.string().min(1, 'relative_path must not be empty'),
17
+ start_line: z
18
+ .number()
19
+ .int()
20
+ .min(0, 'start_line must be non-negative')
21
+ .optional()
22
+ .default(0),
23
+ end_line: z
24
+ .number()
25
+ .int()
26
+ .min(0, 'end_line must be non-negative')
27
+ .nullable()
28
+ .optional(),
29
+ max_answer_chars: z
30
+ .number()
31
+ .int()
32
+ .gte(-1, 'max_answer_chars must be -1 or a positive integer')
33
+ .optional()
34
+ });
35
+ async apply(args) {
36
+ const { relative_path, start_line = 0, end_line = null, max_answer_chars = -1 } = args;
37
+ const project = this.project;
38
+ await validateRelativePath(project, relative_path, this.getProjectRoot());
39
+ const fileContents = await readProjectFile(project, relative_path, this.getProjectRoot());
40
+ const lines = splitIntoLines(fileContents);
41
+ let selected;
42
+ if (end_line === null || end_line === undefined) {
43
+ selected = lines.slice(start_line);
44
+ }
45
+ else {
46
+ recordLinesRead(this.linesRead, relative_path, [start_line, end_line]);
47
+ selected = lines.slice(start_line, end_line + 1);
48
+ }
49
+ const result = selected.join('\n');
50
+ return this._limitLength(result, max_answer_chars);
51
+ }
52
+ }
53
+ export class CreateTextFileTool extends Tool {
54
+ static markers = new Set([ToolMarkerCanEdit]);
55
+ static description = 'Creates or overwrites a text file in the project directory.';
56
+ static inputSchema = z.object({
57
+ relative_path: z.string().min(1, 'relative_path must not be empty'),
58
+ content: z.string()
59
+ });
60
+ async apply(args) {
61
+ const { relative_path, content } = args;
62
+ const projectRoot = this.getProjectRoot();
63
+ const absolutePath = path.resolve(projectRoot, relative_path);
64
+ const exists = fs.existsSync(absolutePath);
65
+ if (exists) {
66
+ await validateRelativePath(this.project, relative_path, projectRoot);
67
+ }
68
+ else if (!isPathInsideRoot(absolutePath, projectRoot)) {
69
+ throw new Error(`Cannot create file outside of the project directory, got relative_path='${relative_path}'`);
70
+ }
71
+ await fs.promises.mkdir(path.dirname(absolutePath), { recursive: true });
72
+ await fs.promises.writeFile(absolutePath, content, { encoding: 'utf-8' });
73
+ let answer = `File created: ${relative_path}.`;
74
+ if (exists) {
75
+ answer += ' Overwrote existing file.';
76
+ }
77
+ return JSON.stringify(answer);
78
+ }
79
+ }
80
+ export class ListDirTool extends Tool {
81
+ static description = 'Lists files and directories for a given relative path. Honors gitignore rules when requested.';
82
+ static inputSchema = z.object({
83
+ relative_path: z.string().min(1, 'relative_path must not be empty'),
84
+ recursive: z.boolean(),
85
+ skip_ignored_files: z.boolean().optional(),
86
+ max_answer_chars: z
87
+ .number()
88
+ .int()
89
+ .gte(-1, 'max_answer_chars must be -1 or a positive integer')
90
+ .optional()
91
+ });
92
+ async apply(args) {
93
+ const { relative_path, recursive, skip_ignored_files = false, max_answer_chars = -1 } = args;
94
+ const project = this.project;
95
+ const pathExists = await projectRelativePathExists(project, relative_path, this.getProjectRoot());
96
+ if (!pathExists) {
97
+ const payload = JSON.stringify({
98
+ error: `Directory not found: ${relative_path}`,
99
+ project_root: this.getProjectRoot(),
100
+ hint: 'Check if the path is correct relative to the project root'
101
+ });
102
+ return this._limitLength(payload, max_answer_chars);
103
+ }
104
+ await validateRelativePath(project, relative_path, this.getProjectRoot());
105
+ const targetPath = path.join(this.getProjectRoot(), relative_path);
106
+ const predicate = skip_ignored_files ? projectIgnorePredicate(project) : undefined;
107
+ const result = scanDirectory(targetPath, recursive, this.getProjectRoot(), predicate, predicate);
108
+ const payload = JSON.stringify({
109
+ dirs: result.directories,
110
+ files: result.files
111
+ });
112
+ return this._limitLength(payload, max_answer_chars);
113
+ }
114
+ }
115
+ export class FindFileTool extends Tool {
116
+ static description = 'Finds files within a directory tree that match a filename mask while respecting ignored paths.';
117
+ static inputSchema = z.object({
118
+ file_mask: z.string().min(1, 'file_mask must not be empty'),
119
+ relative_path: z.string().min(1, 'relative_path must not be empty')
120
+ });
121
+ async apply(args) {
122
+ const { file_mask, relative_path } = args;
123
+ const project = this.project;
124
+ await validateRelativePath(project, relative_path, this.getProjectRoot());
125
+ const dirToScan = path.join(this.getProjectRoot(), relative_path);
126
+ const ignorePredicate = projectIgnorePredicate(project);
127
+ const { files } = scanDirectory(dirToScan, true, this.getProjectRoot(), ignorePredicate, (absolutePath) => {
128
+ if (ignorePredicate?.(absolutePath)) {
129
+ return true;
130
+ }
131
+ const filename = path.basename(absolutePath);
132
+ return !matchesFileMask(file_mask, filename);
133
+ });
134
+ return JSON.stringify({ files });
135
+ }
136
+ }
137
+ export class ReplaceRegexTool extends Tool {
138
+ static markers = new Set([ToolMarkerCanEdit]);
139
+ static description = 'Replaces one or more regex matches within a file. Falls back to Python-style DOTALL matching.';
140
+ static inputSchema = z.object({
141
+ relative_path: z.string().min(1, 'relative_path must not be empty'),
142
+ regex: z.string().min(1, 'regex must not be empty'),
143
+ repl: z.string(),
144
+ allow_multiple_occurrences: z.boolean().optional()
145
+ });
146
+ async apply(args) {
147
+ const { relative_path, regex, repl, allow_multiple_occurrences = false } = args;
148
+ await validateRelativePath(this.project, relative_path, this.getProjectRoot());
149
+ return EditedFileContext.use(relative_path, this.agent, (context) => {
150
+ const original = context.getOriginalContent();
151
+ let count = 0;
152
+ let compiled;
153
+ try {
154
+ compiled = new RegExp(regex, 'gms');
155
+ }
156
+ catch (error) {
157
+ throw new Error(`Invalid regex '${regex}': ${error.message}`);
158
+ }
159
+ const updated = original.replace(compiled, () => {
160
+ count += 1;
161
+ return repl;
162
+ });
163
+ if (count === 0) {
164
+ return `Error: No matches found for regex '${regex}' in file '${relative_path}'.`;
165
+ }
166
+ if (!allow_multiple_occurrences && count > 1) {
167
+ return (`Error: Regex '${regex}' matches ${count} occurrences in file '${relative_path}'. ` +
168
+ 'Please revise the regex to be more specific or enable allow_multiple_occurrences if this is expected.');
169
+ }
170
+ context.setUpdatedContent(updated);
171
+ return SUCCESS_RESULT;
172
+ });
173
+ }
174
+ }
175
+ export class DeleteLinesTool extends Tool {
176
+ static markers = new Set([ToolMarkerCanEdit, ToolMarkerOptional]);
177
+ static description = 'Deletes a range of lines within a file. Requires the same lines to be read beforehand.';
178
+ static inputSchema = z.object({
179
+ relative_path: z.string().min(1, 'relative_path must not be empty'),
180
+ start_line: z
181
+ .number()
182
+ .int()
183
+ .min(0, 'start_line must be non-negative'),
184
+ end_line: z
185
+ .number()
186
+ .int()
187
+ .min(0, 'end_line must be non-negative')
188
+ });
189
+ async apply(args) {
190
+ const { relative_path, start_line, end_line } = args;
191
+ if (!linesWereRead(this.linesRead, relative_path, [start_line, end_line])) {
192
+ const toolName = ReadFileTool.getNameFromCls();
193
+ return `Error: Must call \`${toolName}\` first to read exactly the affected lines.`;
194
+ }
195
+ const editor = this.createCodeEditor();
196
+ await callEditorMethod(editor, ['deleteLines', 'delete_lines'], relative_path, start_line, end_line);
197
+ return SUCCESS_RESULT;
198
+ }
199
+ }
200
+ export class ReplaceLinesTool extends Tool {
201
+ static markers = new Set([ToolMarkerCanEdit, ToolMarkerOptional]);
202
+ static description = 'Replaces a range of lines within a file. Requires the range to be read beforehand.';
203
+ static inputSchema = z.object({
204
+ relative_path: z.string().min(1, 'relative_path must not be empty'),
205
+ start_line: z
206
+ .number()
207
+ .int()
208
+ .min(0, 'start_line must be non-negative'),
209
+ end_line: z
210
+ .number()
211
+ .int()
212
+ .min(0, 'end_line must be non-negative'),
213
+ content: z.string()
214
+ });
215
+ async apply(args) {
216
+ const { relative_path, start_line, end_line, content } = args;
217
+ const normalizedContent = content.endsWith('\n') ? content : `${content}\n`;
218
+ const deleteTool = getToolInstance(this.agent, DeleteLinesTool);
219
+ const deleteResult = await Promise.resolve(deleteTool.apply({ relative_path, start_line, end_line }));
220
+ if (deleteResult !== SUCCESS_RESULT) {
221
+ return deleteResult;
222
+ }
223
+ const insertTool = getToolInstance(this.agent, InsertAtLineTool);
224
+ await Promise.resolve(insertTool.apply({ relative_path, line: start_line, content: normalizedContent }));
225
+ return SUCCESS_RESULT;
226
+ }
227
+ }
228
+ export class InsertAtLineTool extends Tool {
229
+ static markers = new Set([ToolMarkerCanEdit, ToolMarkerOptional]);
230
+ static description = 'Inserts content at a specific line. The insertion will push existing content down.';
231
+ static inputSchema = z.object({
232
+ relative_path: z.string().min(1, 'relative_path must not be empty'),
233
+ line: z
234
+ .number()
235
+ .int()
236
+ .min(0, 'line must be non-negative'),
237
+ content: z.string()
238
+ });
239
+ async apply(args) {
240
+ const { relative_path, line, content } = args;
241
+ const normalizedContent = content.endsWith('\n') ? content : `${content}\n`;
242
+ const editor = this.createCodeEditor();
243
+ await callEditorMethod(editor, ['insertAtLine', 'insert_at_line'], relative_path, line, normalizedContent);
244
+ return SUCCESS_RESULT;
245
+ }
246
+ }
247
+ export class SearchForPatternTool extends Tool {
248
+ static description = 'Searches files for a substring/regex pattern, returning grouped matches with optional context.';
249
+ static inputSchema = z.object({
250
+ substring_pattern: z.string().min(1, 'substring_pattern must not be empty'),
251
+ context_lines_before: z
252
+ .number()
253
+ .int()
254
+ .min(0, 'context_lines_before must be non-negative')
255
+ .optional()
256
+ .default(0),
257
+ context_lines_after: z
258
+ .number()
259
+ .int()
260
+ .min(0, 'context_lines_after must be non-negative')
261
+ .optional()
262
+ .default(0),
263
+ paths_include_glob: z.string().optional(),
264
+ paths_exclude_glob: z.string().optional(),
265
+ relative_path: z.string().optional().default(''),
266
+ restrict_search_to_code_files: z.boolean().optional().default(false),
267
+ max_answer_chars: z
268
+ .number()
269
+ .int()
270
+ .gte(-1, 'max_answer_chars must be -1 or a positive integer')
271
+ .optional()
272
+ });
273
+ async apply(args) {
274
+ const { substring_pattern, context_lines_before = 0, context_lines_after = 0, paths_include_glob, paths_exclude_glob, relative_path = '', restrict_search_to_code_files = false, max_answer_chars = -1 } = args;
275
+ const absolutePath = path.join(this.getProjectRoot(), relative_path);
276
+ if (!fs.existsSync(absolutePath)) {
277
+ throw new Error(`Relative path ${relative_path} does not exist.`);
278
+ }
279
+ let matches;
280
+ const project = this.project;
281
+ if (restrict_search_to_code_files) {
282
+ matches = await searchWithProjectSourceFiles(project, {
283
+ pattern: substring_pattern,
284
+ relative_path,
285
+ context_lines_before,
286
+ context_lines_after,
287
+ paths_include_glob,
288
+ paths_exclude_glob
289
+ });
290
+ }
291
+ else {
292
+ let relativePathsToSearch;
293
+ if (fs.statSync(absolutePath).isFile()) {
294
+ relativePathsToSearch = [relative_path];
295
+ }
296
+ else {
297
+ const ignorePredicate = projectIgnorePredicate(project);
298
+ const { files } = scanDirectory(absolutePath, true, this.getProjectRoot(), ignorePredicate, ignorePredicate);
299
+ relativePathsToSearch = files;
300
+ }
301
+ matches = searchFiles(relativePathsToSearch, substring_pattern, {
302
+ rootPath: this.getProjectRoot(),
303
+ contextLinesBefore: context_lines_before,
304
+ contextLinesAfter: context_lines_after,
305
+ pathsIncludeGlob: paths_include_glob,
306
+ pathsExcludeGlob: paths_exclude_glob
307
+ });
308
+ }
309
+ const grouped = new Map();
310
+ for (const match of matches) {
311
+ const key = match.sourceFilePath ?? relative_path ?? '';
312
+ if (!grouped.has(key)) {
313
+ grouped.set(key, []);
314
+ }
315
+ grouped.get(key).push(match.toDisplayString(true));
316
+ }
317
+ const payload = JSON.stringify(Object.fromEntries(grouped));
318
+ return this._limitLength(payload, max_answer_chars);
319
+ }
320
+ }
321
+ function splitIntoLines(content) {
322
+ const parts = content.split('\n');
323
+ for (let index = 0; index < parts.length - 1; index += 1) {
324
+ const current = parts[index];
325
+ if (current?.endsWith('\r')) {
326
+ parts[index] = current.slice(0, -1);
327
+ }
328
+ }
329
+ return parts;
330
+ }
331
+ async function validateRelativePath(project, relativePath, projectRoot) {
332
+ if (typeof project.validateRelativePath === 'function') {
333
+ await Promise.resolve(project.validateRelativePath(relativePath));
334
+ return;
335
+ }
336
+ if (typeof project.validate_relative_path === 'function') {
337
+ await Promise.resolve(project.validate_relative_path(relativePath));
338
+ return;
339
+ }
340
+ if (!isPathInsideRoot(path.resolve(projectRoot, relativePath), projectRoot)) {
341
+ throw new Error(`Path '${relativePath}' escapes the project root.`);
342
+ }
343
+ }
344
+ async function readProjectFile(project, relativePath, projectRoot) {
345
+ if (typeof project.readFile === 'function') {
346
+ return Promise.resolve(project.readFile(relativePath));
347
+ }
348
+ if (typeof project.read_file === 'function') {
349
+ return Promise.resolve(project.read_file(relativePath));
350
+ }
351
+ const absolutePath = path.resolve(projectRoot, relativePath);
352
+ const encoding = resolveProjectEncoding(project);
353
+ return fs.promises.readFile(absolutePath, { encoding });
354
+ }
355
+ async function projectRelativePathExists(project, relativePath, projectRoot) {
356
+ if (typeof project.relativePathExists === 'function') {
357
+ return Promise.resolve(project.relativePathExists(relativePath));
358
+ }
359
+ if (typeof project.relative_path_exists === 'function') {
360
+ return Promise.resolve(project.relative_path_exists(relativePath));
361
+ }
362
+ const candidate = path.resolve(projectRoot, relativePath);
363
+ return fs.existsSync(candidate);
364
+ }
365
+ function projectIgnorePredicate(project) {
366
+ if (typeof project.isIgnoredPath === 'function') {
367
+ const fn = project.isIgnoredPath.bind(project);
368
+ return (absolutePath) => fn(absolutePath);
369
+ }
370
+ if (typeof project.is_ignored_path === 'function') {
371
+ const fn = project.is_ignored_path.bind(project);
372
+ return (absolutePath) => fn(absolutePath);
373
+ }
374
+ return undefined;
375
+ }
376
+ function matchesFileMask(mask, filename) {
377
+ let pattern = '';
378
+ for (let index = 0; index < mask.length; index += 1) {
379
+ const char = mask[index];
380
+ if (!char) {
381
+ continue;
382
+ }
383
+ if (char === '*') {
384
+ pattern += '.*';
385
+ }
386
+ else if (char === '?') {
387
+ pattern += '.';
388
+ }
389
+ else if (char === '\\') {
390
+ index += 1;
391
+ if (index < mask.length) {
392
+ const nextChar = mask[index];
393
+ if (nextChar) {
394
+ pattern += escapeRegExpChar(nextChar);
395
+ }
396
+ else {
397
+ pattern += '\\\\';
398
+ }
399
+ }
400
+ else {
401
+ pattern += '\\\\';
402
+ }
403
+ }
404
+ else {
405
+ pattern += escapeRegExpChar(char);
406
+ }
407
+ }
408
+ const regex = new RegExp(`^${pattern}$`);
409
+ return regex.test(filename);
410
+ }
411
+ function recordLinesRead(tracker, relativePath, range) {
412
+ if (!tracker) {
413
+ return;
414
+ }
415
+ if (typeof tracker.addLinesRead === 'function') {
416
+ tracker.addLinesRead(relativePath, range);
417
+ }
418
+ else if (typeof tracker.add_lines_read === 'function') {
419
+ tracker.add_lines_read(relativePath, range);
420
+ }
421
+ }
422
+ function linesWereRead(tracker, relativePath, range) {
423
+ if (!tracker) {
424
+ return false;
425
+ }
426
+ if (typeof tracker.wereLinesRead === 'function') {
427
+ return tracker.wereLinesRead(relativePath, range);
428
+ }
429
+ if (typeof tracker.were_lines_read === 'function') {
430
+ return tracker.were_lines_read(relativePath, range);
431
+ }
432
+ return false;
433
+ }
434
+ function isPathInsideRoot(candidatePath, root) {
435
+ const normalizedRoot = path.resolve(root);
436
+ const normalizedCandidate = path.resolve(candidatePath);
437
+ const relative = path.relative(normalizedRoot, normalizedCandidate);
438
+ return !relative.startsWith('..') && !path.isAbsolute(relative);
439
+ }
440
+ async function callEditorMethod(editor, methodCandidates, ...args) {
441
+ for (const candidate of methodCandidates) {
442
+ const method = editor[candidate];
443
+ if (typeof method === 'function') {
444
+ const callable = method;
445
+ await Promise.resolve(callable.apply(editor, args));
446
+ return;
447
+ }
448
+ }
449
+ throw new Error(`Code editor does not implement any of the expected methods: ${methodCandidates.join(', ')}`);
450
+ }
451
+ function getToolInstance(agent, toolClass) {
452
+ const lookupAgent = agent;
453
+ if (typeof lookupAgent.getTool === 'function') {
454
+ return lookupAgent.getTool(toolClass);
455
+ }
456
+ if (typeof lookupAgent.get_tool === 'function') {
457
+ return lookupAgent.get_tool(toolClass);
458
+ }
459
+ const FallbackCtor = toolClass;
460
+ return new FallbackCtor(agent);
461
+ }
462
+ async function searchWithProjectSourceFiles(project, options) {
463
+ if (typeof project.searchSourceFilesForPattern === 'function') {
464
+ const result = project.searchSourceFilesForPattern(options);
465
+ return Promise.resolve(result);
466
+ }
467
+ if (typeof project.search_source_files_for_pattern === 'function') {
468
+ const result = project.search_source_files_for_pattern(options.pattern, options.relative_path, options.context_lines_before, options.context_lines_after, options.paths_include_glob, options.paths_exclude_glob);
469
+ return Promise.resolve(result);
470
+ }
471
+ const relativePaths = projectRelativePathList(project, options.relative_path ?? '');
472
+ return searchFiles(relativePaths, options.pattern, {
473
+ rootPath: (project.projectRoot ?? projectRootFallback(project)) ?? '',
474
+ contextLinesBefore: options.context_lines_before,
475
+ contextLinesAfter: options.context_lines_after,
476
+ pathsIncludeGlob: options.paths_include_glob ?? undefined,
477
+ pathsExcludeGlob: options.paths_exclude_glob ?? undefined
478
+ });
479
+ }
480
+ function projectRelativePathList(project, relativePath) {
481
+ const projectRoot = project.projectRoot ?? projectRootFallback(project);
482
+ if (!projectRoot) {
483
+ return [];
484
+ }
485
+ const absolute = path.join(projectRoot, relativePath);
486
+ const predicate = projectIgnorePredicate(project);
487
+ if (fs.existsSync(absolute)) {
488
+ const stats = fs.statSync(absolute);
489
+ if (stats.isFile()) {
490
+ return [relativePath];
491
+ }
492
+ if (stats.isDirectory()) {
493
+ const { files } = scanDirectory(absolute, true, projectRoot, predicate, predicate);
494
+ return files;
495
+ }
496
+ }
497
+ return [];
498
+ }
499
+ function projectRootFallback(project) {
500
+ if (typeof project.projectRoot === 'string') {
501
+ return project.projectRoot;
502
+ }
503
+ const candidate = project.project_root;
504
+ if (typeof candidate === 'string') {
505
+ return candidate;
506
+ }
507
+ return undefined;
508
+ }
509
+ function escapeRegExpChar(char) {
510
+ return char.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
511
+ }
@@ -0,0 +1,7 @@
1
+ export * from './cmd_tools.js';
2
+ export * from './config_tools.js';
3
+ export * from './file_tools.js';
4
+ export * from './memory_tools.js';
5
+ export * from './symbol_tools.js';
6
+ export * from './tools_base.js';
7
+ export * from './workflow_tools.js';
@@ -0,0 +1,7 @@
1
+ export * from './cmd_tools.js';
2
+ export * from './config_tools.js';
3
+ export * from './file_tools.js';
4
+ export * from './memory_tools.js';
5
+ export * from './symbol_tools.js';
6
+ export * from './tools_base.js';
7
+ export * from './workflow_tools.js';
@@ -0,0 +1,60 @@
1
+ import { z } from 'zod';
2
+ import { Tool } from './tools_base.js';
3
+ interface WriteMemoryInput {
4
+ memory_name: string;
5
+ content: string;
6
+ max_answer_chars?: number;
7
+ }
8
+ interface MemoryNameInput {
9
+ memory_file_name: string;
10
+ max_answer_chars?: number;
11
+ }
12
+ export declare class WriteMemoryTool extends Tool {
13
+ static readonly markers: Set<"can-edit">;
14
+ static readonly description = "Writes a named markdown memory entry to Serena's project-specific memory store.";
15
+ static readonly inputSchema: z.ZodObject<{
16
+ memory_name: z.ZodString;
17
+ content: z.ZodString;
18
+ max_answer_chars: z.ZodOptional<z.ZodNumber>;
19
+ }, "strip", z.ZodTypeAny, {
20
+ content: string;
21
+ memory_name: string;
22
+ max_answer_chars?: number | undefined;
23
+ }, {
24
+ content: string;
25
+ memory_name: string;
26
+ max_answer_chars?: number | undefined;
27
+ }>;
28
+ apply(args: WriteMemoryInput): Promise<string>;
29
+ }
30
+ export declare class ReadMemoryTool extends Tool {
31
+ static readonly description = "Reads the content of a memory file from Serena's project-specific memory store.";
32
+ static readonly inputSchema: z.ZodObject<{
33
+ memory_file_name: z.ZodString;
34
+ max_answer_chars: z.ZodOptional<z.ZodNumber>;
35
+ }, "strip", z.ZodTypeAny, {
36
+ memory_file_name: string;
37
+ max_answer_chars?: number | undefined;
38
+ }, {
39
+ memory_file_name: string;
40
+ max_answer_chars?: number | undefined;
41
+ }>;
42
+ apply(args: MemoryNameInput): Promise<string>;
43
+ }
44
+ export declare class ListMemoriesTool extends Tool {
45
+ static readonly description = "Lists the available memories in Serena's project-specific memory store as a JSON array.";
46
+ apply(_args?: Record<string, unknown>): Promise<string>;
47
+ }
48
+ export declare class DeleteMemoryTool extends Tool {
49
+ static readonly markers: Set<"can-edit">;
50
+ static readonly description = "Deletes a memory file from Serena's project-specific memory store.";
51
+ static readonly inputSchema: z.ZodObject<{
52
+ memory_file_name: z.ZodString;
53
+ }, "strip", z.ZodTypeAny, {
54
+ memory_file_name: string;
55
+ }, {
56
+ memory_file_name: string;
57
+ }>;
58
+ apply(args: MemoryNameInput): Promise<string>;
59
+ }
60
+ export {};