@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,267 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createSerenaLogger } from './util/logging.js';
4
+ const { logger: log } = createSerenaLogger({ name: 'serena.text_utils' });
5
+ export var LineType;
6
+ (function (LineType) {
7
+ LineType["MATCH"] = "match";
8
+ LineType["BEFORE_MATCH"] = "prefix";
9
+ LineType["AFTER_MATCH"] = "postfix";
10
+ })(LineType || (LineType = {}));
11
+ export class TextLine {
12
+ lineNumber;
13
+ lineContent;
14
+ matchType;
15
+ constructor(options) {
16
+ this.lineNumber = options.lineNumber;
17
+ this.lineContent = options.lineContent;
18
+ this.matchType = options.matchType;
19
+ }
20
+ getDisplayPrefix() {
21
+ return this.matchType === LineType.MATCH ? ' >' : '...';
22
+ }
23
+ formatLine(includeLineNumbers = true) {
24
+ let prefix = this.getDisplayPrefix();
25
+ if (includeLineNumbers) {
26
+ const lineNum = String(this.lineNumber).padStart(4, ' ');
27
+ prefix = `${prefix}${lineNum}`;
28
+ }
29
+ return `${prefix}:${this.lineContent}`;
30
+ }
31
+ }
32
+ export class MatchedConsecutiveLines {
33
+ lines;
34
+ sourceFilePath;
35
+ linesBeforeMatched;
36
+ matchedLines;
37
+ linesAfterMatched;
38
+ constructor(options) {
39
+ if (!options.lines.length) {
40
+ throw new Error('At least one matched line is required.');
41
+ }
42
+ this.lines = options.lines;
43
+ this.sourceFilePath = options.sourceFilePath;
44
+ this.linesBeforeMatched = [];
45
+ this.matchedLines = [];
46
+ this.linesAfterMatched = [];
47
+ for (const line of options.lines) {
48
+ if (line.matchType === LineType.BEFORE_MATCH) {
49
+ this.linesBeforeMatched.push(line);
50
+ }
51
+ else if (line.matchType === LineType.MATCH) {
52
+ this.matchedLines.push(line);
53
+ }
54
+ else if (line.matchType === LineType.AFTER_MATCH) {
55
+ this.linesAfterMatched.push(line);
56
+ }
57
+ }
58
+ if (this.matchedLines.length === 0) {
59
+ throw new Error('At least one matched line is required.');
60
+ }
61
+ }
62
+ get startLine() {
63
+ return this.lines[0]?.lineNumber ?? 0;
64
+ }
65
+ get endLine() {
66
+ return this.lines[this.lines.length - 1]?.lineNumber ?? 0;
67
+ }
68
+ get numMatchedLines() {
69
+ return this.matchedLines.length;
70
+ }
71
+ toDisplayString(includeLineNumbers = true) {
72
+ return this.lines.map((line) => line.formatLine(includeLineNumbers)).join('\n');
73
+ }
74
+ static fromFileContents(options) {
75
+ const { fileContents, line, contextLinesBefore = 0, contextLinesAfter = 0, sourceFilePath } = options;
76
+ const lineContents = splitIntoLinesPreservingEnding(fileContents);
77
+ const startLine = Math.max(0, line - contextLinesBefore);
78
+ const endLine = Math.min(lineContents.length - 1, line + contextLinesAfter);
79
+ const lines = [];
80
+ for (let index = startLine; index <= endLine; index += 1) {
81
+ const matchType = index < line
82
+ ? LineType.BEFORE_MATCH
83
+ : index > line
84
+ ? LineType.AFTER_MATCH
85
+ : LineType.MATCH;
86
+ lines.push(new TextLine({
87
+ lineNumber: index,
88
+ lineContent: lineContents[index] ?? '',
89
+ matchType
90
+ }));
91
+ }
92
+ return new MatchedConsecutiveLines({ lines, sourceFilePath });
93
+ }
94
+ }
95
+ const DEFAULT_FILE_READER = (absolutePath) => {
96
+ return fs.readFileSync(absolutePath, 'utf-8');
97
+ };
98
+ export function searchFiles(relativeFilePaths, pattern, options = {}) {
99
+ const { rootPath = '', fileReader = DEFAULT_FILE_READER, contextLinesBefore = 0, contextLinesAfter = 0, pathsIncludeGlob, pathsExcludeGlob } = options;
100
+ const filteredPaths = relativeFilePaths.filter((candidate) => {
101
+ if (pathsIncludeGlob && !globMatch(pathsIncludeGlob, candidate)) {
102
+ log.debug(`Skipping ${candidate}: does not match include pattern ${pathsIncludeGlob}`);
103
+ return false;
104
+ }
105
+ if (pathsExcludeGlob && globMatch(pathsExcludeGlob, candidate)) {
106
+ log.debug(`Skipping ${candidate}: matches exclude pattern ${pathsExcludeGlob}`);
107
+ return false;
108
+ }
109
+ return true;
110
+ });
111
+ log.info(`Processing ${filteredPaths.length} files.`);
112
+ const matches = [];
113
+ const skipped = [];
114
+ for (const relativePath of filteredPaths) {
115
+ try {
116
+ const absolutePath = rootPath ? path.join(rootPath, relativePath) : relativePath;
117
+ const fileContents = fileReader(absolutePath);
118
+ const results = searchText(fileContents, pattern, {
119
+ contextLinesBefore,
120
+ contextLinesAfter
121
+ }).map((item) => new MatchedConsecutiveLines({
122
+ lines: item.lines,
123
+ sourceFilePath: relativePath
124
+ }));
125
+ if (results.length > 0) {
126
+ log.debug(`Found ${results.length} matches in ${relativePath}`);
127
+ }
128
+ matches.push(...results);
129
+ }
130
+ catch (error) {
131
+ skipped.push({ path: relativePath, error });
132
+ }
133
+ }
134
+ if (skipped.length > 0) {
135
+ log.debug(`Failed to read ${skipped.length} files`, skipped);
136
+ }
137
+ log.info(`Found ${matches.length} total matches across ${filteredPaths.length} files`);
138
+ return matches;
139
+ }
140
+ function searchText(content, pattern, options = {}) {
141
+ const { contextLinesBefore = 0, contextLinesAfter = 0 } = options;
142
+ let regex;
143
+ try {
144
+ regex = new RegExp(pattern, 'gms');
145
+ }
146
+ catch (error) {
147
+ throw new Error(`Invalid regular expression pattern: ${error.message}`);
148
+ }
149
+ const matches = [];
150
+ const lines = splitIntoLinesPreservingEnding(content);
151
+ const lineOffsets = computeLineOffsets(content);
152
+ for (const match of content.matchAll(regex)) {
153
+ if (match.index === undefined) {
154
+ continue;
155
+ }
156
+ const startIndex = match.index;
157
+ const endIndex = startIndex + match[0].length;
158
+ const startLine = findLineNumberForOffset(lineOffsets, startIndex);
159
+ const endLine = findLineNumberForOffset(lineOffsets, Math.max(startIndex, endIndex - 1));
160
+ const beforeStart = Math.max(0, startLine - contextLinesBefore);
161
+ const afterEnd = Math.min(lines.length - 1, endLine + contextLinesAfter);
162
+ const consecutiveLines = [];
163
+ for (let lineNumber = beforeStart; lineNumber <= afterEnd; lineNumber += 1) {
164
+ const matchType = lineNumber < startLine
165
+ ? LineType.BEFORE_MATCH
166
+ : lineNumber > endLine
167
+ ? LineType.AFTER_MATCH
168
+ : LineType.MATCH;
169
+ consecutiveLines.push(new TextLine({
170
+ lineNumber,
171
+ lineContent: lines[lineNumber] ?? '',
172
+ matchType
173
+ }));
174
+ }
175
+ matches.push(new MatchedConsecutiveLines({ lines: consecutiveLines }));
176
+ }
177
+ return matches;
178
+ }
179
+ function splitIntoLinesPreservingEnding(input) {
180
+ if (input.length === 0) {
181
+ return [''];
182
+ }
183
+ const parts = input.split('\n');
184
+ for (let index = 0; index < parts.length - 1; index += 1) {
185
+ const current = parts[index];
186
+ if (current?.endsWith('\r')) {
187
+ parts[index] = current.slice(0, -1);
188
+ }
189
+ }
190
+ return parts;
191
+ }
192
+ function computeLineOffsets(content) {
193
+ const offsets = [0];
194
+ for (let index = 0; index < content.length; index += 1) {
195
+ if (content[index] === '\n') {
196
+ offsets.push(index + 1);
197
+ }
198
+ }
199
+ return offsets;
200
+ }
201
+ function findLineNumberForOffset(lineOffsets, offset) {
202
+ let low = 0;
203
+ let high = lineOffsets.length - 1;
204
+ while (low <= high) {
205
+ const mid = Math.floor((low + high) / 2);
206
+ const start = lineOffsets[mid];
207
+ if (start === undefined) {
208
+ break;
209
+ }
210
+ const nextStart = lineOffsets[mid + 1];
211
+ if (offset < start) {
212
+ high = mid - 1;
213
+ }
214
+ else if (nextStart !== undefined && offset >= nextStart) {
215
+ low = mid + 1;
216
+ }
217
+ else {
218
+ return mid;
219
+ }
220
+ }
221
+ return Math.max(0, lineOffsets.length - 1);
222
+ }
223
+ function globMatch(pattern, candidate) {
224
+ if (!pattern) {
225
+ return true;
226
+ }
227
+ const normalizedCandidate = candidate.split(path.sep).join('/');
228
+ const regex = globToRegExp(pattern);
229
+ return regex.test(normalizedCandidate);
230
+ }
231
+ function globToRegExp(pattern) {
232
+ let escaped = '';
233
+ for (let index = 0; index < pattern.length; index += 1) {
234
+ const char = pattern[index];
235
+ if (!char) {
236
+ continue;
237
+ }
238
+ if (char === '*') {
239
+ escaped += '.*';
240
+ }
241
+ else if (char === '?') {
242
+ escaped += '.';
243
+ }
244
+ else if (char === '\\') {
245
+ index += 1;
246
+ if (index < pattern.length) {
247
+ const nextChar = pattern[index];
248
+ if (nextChar) {
249
+ escaped += escapeRegExpChar(nextChar);
250
+ }
251
+ else {
252
+ escaped += '\\\\';
253
+ }
254
+ }
255
+ else {
256
+ escaped += '\\\\';
257
+ }
258
+ }
259
+ else {
260
+ escaped += escapeRegExpChar(char);
261
+ }
262
+ }
263
+ return new RegExp(`^${escaped}$`);
264
+ }
265
+ function escapeRegExpChar(char) {
266
+ return char.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
267
+ }
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ import { Tool } from './tools_base.js';
3
+ interface ExecuteShellCommandInput {
4
+ command: string;
5
+ cwd?: string;
6
+ capture_stderr?: boolean;
7
+ max_answer_chars?: number;
8
+ }
9
+ export declare class ExecuteShellCommandTool extends Tool {
10
+ static readonly markers: Set<"can-edit">;
11
+ static readonly description = "Executes a shell command and returns its stdout/stderr output as JSON.";
12
+ static readonly inputSchema: z.ZodObject<{
13
+ command: z.ZodString;
14
+ cwd: z.ZodOptional<z.ZodString>;
15
+ capture_stderr: z.ZodOptional<z.ZodBoolean>;
16
+ max_answer_chars: z.ZodOptional<z.ZodNumber>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ command: string;
19
+ cwd?: string | undefined;
20
+ capture_stderr?: boolean | undefined;
21
+ max_answer_chars?: number | undefined;
22
+ }, {
23
+ command: string;
24
+ cwd?: string | undefined;
25
+ capture_stderr?: boolean | undefined;
26
+ max_answer_chars?: number | undefined;
27
+ }>;
28
+ apply(args: ExecuteShellCommandInput): Promise<string>;
29
+ private resolveWorkingDirectory;
30
+ }
31
+ export {};
@@ -0,0 +1,48 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { z } from 'zod';
4
+ import { executeShellCommand } from '../util/shell.js';
5
+ import { Tool, ToolMarkerCanEdit } from './tools_base.js';
6
+ const DEFAULT_CAPTURE_STDERR = true;
7
+ export class ExecuteShellCommandTool extends Tool {
8
+ static markers = new Set([ToolMarkerCanEdit]);
9
+ static description = 'Executes a shell command and returns its stdout/stderr output as JSON.';
10
+ static inputSchema = z.object({
11
+ command: z.string().min(1, 'command must not be empty'),
12
+ cwd: z.string().optional(),
13
+ capture_stderr: z.boolean().optional(),
14
+ max_answer_chars: z
15
+ .number()
16
+ .int()
17
+ .gte(-1, 'max_answer_chars must be -1 or a positive integer')
18
+ .optional()
19
+ });
20
+ async apply(args) {
21
+ const { command, cwd, capture_stderr = DEFAULT_CAPTURE_STDERR, max_answer_chars = -1 } = args;
22
+ const resolvedCwd = this.resolveWorkingDirectory(cwd);
23
+ const result = await executeShellCommand(command, {
24
+ cwd: resolvedCwd,
25
+ captureStderr: capture_stderr
26
+ });
27
+ const payload = JSON.stringify({
28
+ stdout: result.stdout,
29
+ stderr: result.stderr ?? null,
30
+ return_code: result.returnCode,
31
+ cwd: result.cwd
32
+ });
33
+ return this._limitLength(payload, max_answer_chars);
34
+ }
35
+ resolveWorkingDirectory(cwd) {
36
+ if (!cwd) {
37
+ return this.getProjectRoot();
38
+ }
39
+ if (path.isAbsolute(cwd)) {
40
+ return cwd;
41
+ }
42
+ const resolved = path.join(this.getProjectRoot(), cwd);
43
+ if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
44
+ throw new Error(`Specified a relative working directory (${cwd}), but the resulting path is not a directory: ${resolved}`);
45
+ }
46
+ return resolved;
47
+ }
48
+ }
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ import { Tool } from './tools_base.js';
3
+ interface ActivateProjectInput {
4
+ project: string;
5
+ }
6
+ interface RemoveProjectInput {
7
+ project_name: string;
8
+ }
9
+ interface SwitchModesInput {
10
+ modes: string[];
11
+ }
12
+ export declare class ActivateProjectTool extends Tool {
13
+ static readonly markers: Set<"does-not-require-active-project">;
14
+ static readonly description = "Activates a project by name or path.";
15
+ static readonly inputSchema: z.ZodObject<{
16
+ project: z.ZodString;
17
+ }, "strip", z.ZodTypeAny, {
18
+ project: string;
19
+ }, {
20
+ project: string;
21
+ }>;
22
+ apply(args: ActivateProjectInput): Promise<string>;
23
+ }
24
+ export declare class RemoveProjectTool extends Tool {
25
+ static readonly markers: Set<"does-not-require-active-project" | "optional">;
26
+ static readonly description = "Removes a project from the Serena configuration.";
27
+ static readonly inputSchema: z.ZodObject<{
28
+ project_name: z.ZodString;
29
+ }, "strip", z.ZodTypeAny, {
30
+ project_name: string;
31
+ }, {
32
+ project_name: string;
33
+ }>;
34
+ apply(args: RemoveProjectInput): Promise<string>;
35
+ }
36
+ export declare class SwitchModesTool extends Tool {
37
+ static readonly markers: Set<"optional">;
38
+ static readonly description = "Activates the desired agent modes by name.";
39
+ static readonly inputSchema: z.ZodObject<{
40
+ modes: z.ZodArray<z.ZodString, "many">;
41
+ }, "strip", z.ZodTypeAny, {
42
+ modes: string[];
43
+ }, {
44
+ modes: string[];
45
+ }>;
46
+ apply(args: SwitchModesInput): Promise<string>;
47
+ }
48
+ export declare class GetCurrentConfigTool extends Tool {
49
+ static readonly description = "Displays the current configuration of the agent, including projects, tools, contexts, and modes.";
50
+ apply(_args?: Record<string, unknown>): Promise<string>;
51
+ }
52
+ export declare const CONFIG_TOOL_CLASSES: (typeof ActivateProjectTool | typeof RemoveProjectTool | typeof SwitchModesTool | typeof GetCurrentConfigTool)[];
53
+ export {};
@@ -0,0 +1,176 @@
1
+ import { z } from 'zod';
2
+ import { SerenaAgentMode } from '../config/context_mode.js';
3
+ import { Tool, ToolMarkerDoesNotRequireActiveProject, ToolMarkerOptional } from './tools_base.js';
4
+ function stringifyUnknown(value, fallback = 'unknown') {
5
+ if (value === undefined || value === null) {
6
+ return fallback;
7
+ }
8
+ if (typeof value === 'string') {
9
+ return value;
10
+ }
11
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint' || typeof value === 'symbol') {
12
+ return String(value);
13
+ }
14
+ if (typeof value === 'function') {
15
+ return '[function]';
16
+ }
17
+ if (typeof value === 'object') {
18
+ try {
19
+ return JSON.stringify(value);
20
+ }
21
+ catch {
22
+ return Object.prototype.toString.call(value);
23
+ }
24
+ }
25
+ return fallback;
26
+ }
27
+ function isIterable(value) {
28
+ return typeof value === 'object' && value !== null && Symbol.iterator in value;
29
+ }
30
+ function resolveLanguageName(language) {
31
+ if (typeof language === 'string') {
32
+ return language;
33
+ }
34
+ if (language && typeof language === 'object' && 'value' in language) {
35
+ const value = language.value;
36
+ if (typeof value === 'string') {
37
+ return value;
38
+ }
39
+ if (value !== undefined && value !== null) {
40
+ return stringifyUnknown(value);
41
+ }
42
+ }
43
+ return stringifyUnknown(language);
44
+ }
45
+ function resolveProjectName(project) {
46
+ const fromConfig = project.projectConfig.projectName ?? project.projectConfig.project_name;
47
+ if (fromConfig && typeof fromConfig === 'string') {
48
+ return fromConfig;
49
+ }
50
+ const directName = project.projectName;
51
+ if (typeof directName === 'string' && directName.length > 0) {
52
+ return directName;
53
+ }
54
+ const snakeCaseName = project.project_name;
55
+ if (typeof snakeCaseName === 'string' && snakeCaseName.length > 0) {
56
+ return snakeCaseName;
57
+ }
58
+ return 'unknown';
59
+ }
60
+ function resolveInitialPrompt(projectConfig) {
61
+ const prompt = projectConfig.initialPrompt ?? projectConfig.initial_prompt;
62
+ if (typeof prompt === 'string' && prompt.trim().length > 0) {
63
+ return prompt;
64
+ }
65
+ return '';
66
+ }
67
+ function resolveProjectYamlPath(project) {
68
+ const camelCase = project.pathToProjectYml;
69
+ if (typeof camelCase === 'function') {
70
+ return camelCase();
71
+ }
72
+ const snakeCase = project.path_to_project_yml;
73
+ if (typeof snakeCase === 'function') {
74
+ return snakeCase();
75
+ }
76
+ return undefined;
77
+ }
78
+ async function listAvailableMemories(manager) {
79
+ const raw = await Promise.resolve(manager.listMemories());
80
+ if (!raw) {
81
+ return [];
82
+ }
83
+ if (Array.isArray(raw)) {
84
+ return raw.map((entry) => stringifyUnknown(entry));
85
+ }
86
+ if (isIterable(raw)) {
87
+ const result = [];
88
+ for (const item of raw) {
89
+ result.push(stringifyUnknown(item));
90
+ }
91
+ return result;
92
+ }
93
+ return [stringifyUnknown(raw)];
94
+ }
95
+ export class ActivateProjectTool extends Tool {
96
+ static markers = new Set([ToolMarkerDoesNotRequireActiveProject]);
97
+ static description = 'Activates a project by name or path.';
98
+ static inputSchema = z.object({
99
+ project: z.string().min(1, 'project must not be empty')
100
+ });
101
+ async apply(args) {
102
+ const activeProject = await Promise.resolve(this.agent.activateProjectFromPathOrName(args.project));
103
+ const isNewlyCreated = Boolean(activeProject.isNewlyCreated ??
104
+ activeProject.is_newly_created ??
105
+ false);
106
+ const projectName = resolveProjectName(activeProject);
107
+ const language = resolveLanguageName(activeProject.projectConfig.language);
108
+ const projectYamlPath = resolveProjectYamlPath(activeProject);
109
+ let result = '';
110
+ if (isNewlyCreated) {
111
+ result =
112
+ `Created and activated a new project with name '${projectName}' at ${activeProject.projectRoot}, language: ${language}. ` +
113
+ "You can activate this project later by name.\n";
114
+ if (projectYamlPath) {
115
+ result += `The project's Serena configuration is in ${projectYamlPath}. In particular, you may want to edit the project name and the initial prompt.`;
116
+ }
117
+ }
118
+ else {
119
+ result = `Activated existing project with name '${projectName}' at ${activeProject.projectRoot}, language: ${language}`;
120
+ }
121
+ const initialPrompt = resolveInitialPrompt(activeProject.projectConfig);
122
+ if (initialPrompt.length > 0) {
123
+ result += `\nAdditional project information:\n ${initialPrompt}`;
124
+ }
125
+ const memoriesManager = this.memoriesManager;
126
+ const memories = await listAvailableMemories(memoriesManager);
127
+ result +=
128
+ `\nAvailable memories:\n ${JSON.stringify(memories)}` +
129
+ 'You should not read these memories directly, but rather use the `read_memory` tool to read them later if needed for the task.';
130
+ const activeTools = this.agent.getActiveToolNames();
131
+ result += `\nAvailable tools:\n ${JSON.stringify(activeTools)}`;
132
+ return result;
133
+ }
134
+ }
135
+ export class RemoveProjectTool extends Tool {
136
+ static markers = new Set([ToolMarkerDoesNotRequireActiveProject, ToolMarkerOptional]);
137
+ static description = 'Removes a project from the Serena configuration.';
138
+ static inputSchema = z.object({
139
+ project_name: z.string().min(1, 'project_name must not be empty')
140
+ });
141
+ async apply(args) {
142
+ const config = this.agent.serenaConfig;
143
+ if (typeof config.removeProject !== 'function') {
144
+ throw new Error('Removing projects is not supported by the active Serena configuration.');
145
+ }
146
+ await Promise.resolve(config.removeProject.call(config, args.project_name));
147
+ return `Successfully removed project '${args.project_name}' from configuration.`;
148
+ }
149
+ }
150
+ export class SwitchModesTool extends Tool {
151
+ static markers = new Set([ToolMarkerOptional]);
152
+ static description = 'Activates the desired agent modes by name.';
153
+ static inputSchema = z.object({
154
+ modes: z.array(z.string().min(1, 'mode name must not be empty')).min(1, 'at least one mode must be provided')
155
+ });
156
+ async apply(args) {
157
+ const modeInstances = args.modes.map((modeName) => SerenaAgentMode.load(modeName));
158
+ await Promise.resolve(this.agent.setModes(modeInstances));
159
+ let result = `Successfully activated modes: ${modeInstances.map((mode) => mode.name).join(', ')}` + '\n';
160
+ result += modeInstances.map((mode) => mode.prompt).join('\n') + '\n';
161
+ result += `Currently active tools: ${this.agent.getActiveToolNames().join(', ')}`;
162
+ return result;
163
+ }
164
+ }
165
+ export class GetCurrentConfigTool extends Tool {
166
+ static description = 'Displays the current configuration of the agent, including projects, tools, contexts, and modes.';
167
+ async apply(_args = {}) {
168
+ return Promise.resolve(this.agent.getCurrentConfigOverview());
169
+ }
170
+ }
171
+ export const CONFIG_TOOL_CLASSES = [
172
+ ActivateProjectTool,
173
+ RemoveProjectTool,
174
+ SwitchModesTool,
175
+ GetCurrentConfigTool
176
+ ];