@4onstudios/iris-agent 0.1.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 (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +690 -0
  3. package/dist/api/acp/acpServer.d.ts +23 -0
  4. package/dist/api/acp/acpServer.js +814 -0
  5. package/dist/api/acp/index.d.ts +2 -0
  6. package/dist/api/acp/index.js +2 -0
  7. package/dist/api/acp/irisClient.d.ts +58 -0
  8. package/dist/api/acp/irisClient.js +214 -0
  9. package/dist/api/agent.d.ts +14 -0
  10. package/dist/api/agent.js +4171 -0
  11. package/dist/api/backendService.d.ts +4 -0
  12. package/dist/api/backendService.js +14 -0
  13. package/dist/api/core/agent/host/AgentContract.d.ts +82 -0
  14. package/dist/api/core/agent/host/AgentContract.js +1 -0
  15. package/dist/api/core/agent/host/AgentRegistry.d.ts +24 -0
  16. package/dist/api/core/agent/host/AgentRegistry.js +125 -0
  17. package/dist/api/core/agent/host/defaultRegistry.d.ts +15 -0
  18. package/dist/api/core/agent/host/defaultRegistry.js +46 -0
  19. package/dist/api/core/agent/host/externalAgentLifecycle.d.ts +65 -0
  20. package/dist/api/core/agent/host/externalAgentLifecycle.js +117 -0
  21. package/dist/api/core/agent/host/hostSessionManager.d.ts +111 -0
  22. package/dist/api/core/agent/host/hostSessionManager.js +352 -0
  23. package/dist/api/core/agent/host/index.d.ts +5 -0
  24. package/dist/api/core/agent/host/index.js +5 -0
  25. package/dist/api/core/agent/index.d.ts +87 -0
  26. package/dist/api/core/agent/index.js +1024 -0
  27. package/dist/api/core/agent/publicContracts.d.ts +35 -0
  28. package/dist/api/core/agent/publicContracts.js +103 -0
  29. package/dist/api/core/agent/tools/applyDiff.d.ts +51 -0
  30. package/dist/api/core/agent/tools/applyDiff.js +131 -0
  31. package/dist/api/core/agent/tools/backgroundTasks.d.ts +240 -0
  32. package/dist/api/core/agent/tools/backgroundTasks.js +313 -0
  33. package/dist/api/core/agent/tools/createDirectory.d.ts +43 -0
  34. package/dist/api/core/agent/tools/createDirectory.js +77 -0
  35. package/dist/api/core/agent/tools/deleteFile.d.ts +37 -0
  36. package/dist/api/core/agent/tools/deleteFile.js +66 -0
  37. package/dist/api/core/agent/tools/editFile.d.ts +73 -0
  38. package/dist/api/core/agent/tools/editFile.js +173 -0
  39. package/dist/api/core/agent/tools/executeCommand.d.ts +59 -0
  40. package/dist/api/core/agent/tools/executeCommand.js +250 -0
  41. package/dist/api/core/agent/tools/fileContent.d.ts +54 -0
  42. package/dist/api/core/agent/tools/fileContent.js +227 -0
  43. package/dist/api/core/agent/tools/findDefinition.d.ts +72 -0
  44. package/dist/api/core/agent/tools/findDefinition.js +109 -0
  45. package/dist/api/core/agent/tools/findReferences.d.ts +86 -0
  46. package/dist/api/core/agent/tools/findReferences.js +135 -0
  47. package/dist/api/core/agent/tools/formatDocument.d.ts +67 -0
  48. package/dist/api/core/agent/tools/formatDocument.js +90 -0
  49. package/dist/api/core/agent/tools/getCodeActions.d.ts +77 -0
  50. package/dist/api/core/agent/tools/getCodeActions.js +97 -0
  51. package/dist/api/core/agent/tools/getCodeCompletion.d.ts +77 -0
  52. package/dist/api/core/agent/tools/getCodeCompletion.js +174 -0
  53. package/dist/api/core/agent/tools/getCodeContext.d.ts +14 -0
  54. package/dist/api/core/agent/tools/getCodeContext.js +282 -0
  55. package/dist/api/core/agent/tools/getSignatureHelp.d.ts +61 -0
  56. package/dist/api/core/agent/tools/getSignatureHelp.js +85 -0
  57. package/dist/api/core/agent/tools/getSymbols.d.ts +50 -0
  58. package/dist/api/core/agent/tools/getSymbols.js +164 -0
  59. package/dist/api/core/agent/tools/getSymbolsLSP.d.ts +58 -0
  60. package/dist/api/core/agent/tools/getSymbolsLSP.js +147 -0
  61. package/dist/api/core/agent/tools/getTypeInfo.d.ts +70 -0
  62. package/dist/api/core/agent/tools/getTypeInfo.js +84 -0
  63. package/dist/api/core/agent/tools/getWorkspaceInfo.d.ts +80 -0
  64. package/dist/api/core/agent/tools/getWorkspaceInfo.js +281 -0
  65. package/dist/api/core/agent/tools/getWorkspaceSymbols.d.ts +53 -0
  66. package/dist/api/core/agent/tools/getWorkspaceSymbols.js +97 -0
  67. package/dist/api/core/agent/tools/grepSearch.d.ts +100 -0
  68. package/dist/api/core/agent/tools/grepSearch.js +211 -0
  69. package/dist/api/core/agent/tools/languageModelToolsIntegration.d.ts +13 -0
  70. package/dist/api/core/agent/tools/languageModelToolsIntegration.js +22 -0
  71. package/dist/api/core/agent/tools/listDirectory.d.ts +66 -0
  72. package/dist/api/core/agent/tools/listDirectory.js +161 -0
  73. package/dist/api/core/agent/tools/mcpTools.d.ts +48 -0
  74. package/dist/api/core/agent/tools/mcpTools.js +442 -0
  75. package/dist/api/core/agent/tools/queryKnowledgeGraph.d.ts +120 -0
  76. package/dist/api/core/agent/tools/queryKnowledgeGraph.js +306 -0
  77. package/dist/api/core/agent/tools/readFile.d.ts +92 -0
  78. package/dist/api/core/agent/tools/readFile.js +337 -0
  79. package/dist/api/core/agent/tools/renameFile.d.ts +49 -0
  80. package/dist/api/core/agent/tools/renameFile.js +86 -0
  81. package/dist/api/core/agent/tools/renameSymbol.d.ts +70 -0
  82. package/dist/api/core/agent/tools/renameSymbol.js +106 -0
  83. package/dist/api/core/agent/tools/runTerminalCommand.d.ts +72 -0
  84. package/dist/api/core/agent/tools/runTerminalCommand.js +69 -0
  85. package/dist/api/core/agent/tools/searchFiles.d.ts +82 -0
  86. package/dist/api/core/agent/tools/searchFiles.js +280 -0
  87. package/dist/api/core/agent/tools/terminalAutoApprove.d.ts +3 -0
  88. package/dist/api/core/agent/tools/terminalAutoApprove.js +676 -0
  89. package/dist/api/core/agent/tools/treeTraversal.d.ts +60 -0
  90. package/dist/api/core/agent/tools/treeTraversal.js +217 -0
  91. package/dist/api/core/agent/tools/webSearch.d.ts +37 -0
  92. package/dist/api/core/agent/tools/webSearch.js +80 -0
  93. package/dist/api/core/agent/tools/writeFile.d.ts +59 -0
  94. package/dist/api/core/agent/tools/writeFile.js +97 -0
  95. package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.d.ts +12 -0
  96. package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.js +67 -0
  97. package/dist/api/core/agent/utils/diffUtils.d.ts +14 -0
  98. package/dist/api/core/agent/utils/diffUtils.js +39 -0
  99. package/dist/api/core/agent/utils/environmentSnapshot.d.ts +42 -0
  100. package/dist/api/core/agent/utils/environmentSnapshot.js +213 -0
  101. package/dist/api/core/agent/utils/errorRecovery.d.ts +49 -0
  102. package/dist/api/core/agent/utils/errorRecovery.js +229 -0
  103. package/dist/api/core/agent/utils/multimodalTokenLimiter.d.ts +8 -0
  104. package/dist/api/core/agent/utils/multimodalTokenLimiter.js +238 -0
  105. package/dist/api/core/agent/utils/openRouterModelSettings.d.ts +12 -0
  106. package/dist/api/core/agent/utils/openRouterModelSettings.js +34 -0
  107. package/dist/api/core/agent/utils/pathRecovery.d.ts +1 -0
  108. package/dist/api/core/agent/utils/pathRecovery.js +29 -0
  109. package/dist/api/core/agent/utils/repoMapIndex.d.ts +3 -0
  110. package/dist/api/core/agent/utils/repoMapIndex.js +197 -0
  111. package/dist/api/core/agent/utils/skillsDiscovery.d.ts +10 -0
  112. package/dist/api/core/agent/utils/skillsDiscovery.js +50 -0
  113. package/dist/api/core/agent/utils/toolCallBudget.d.ts +9 -0
  114. package/dist/api/core/agent/utils/toolCallBudget.js +55 -0
  115. package/dist/api/core/agent/utils/toolLifecycle.d.ts +30 -0
  116. package/dist/api/core/agent/utils/toolLifecycle.js +361 -0
  117. package/dist/api/core/agent/utils/toolResultSafetyProcessor.d.ts +12 -0
  118. package/dist/api/core/agent/utils/toolResultSafetyProcessor.js +57 -0
  119. package/dist/api/core/agent/utils/workspaceMutationBridge.d.ts +13 -0
  120. package/dist/api/core/agent/utils/workspaceMutationBridge.js +149 -0
  121. package/dist/api/core/agent/utils/workspacePathGuard.d.ts +7 -0
  122. package/dist/api/core/agent/utils/workspacePathGuard.js +79 -0
  123. package/dist/api/core/containers/chat/toolResultSerialization.d.ts +8 -0
  124. package/dist/api/core/containers/chat/toolResultSerialization.js +71 -0
  125. package/dist/api/core/library/BrowserManager.d.ts +11 -0
  126. package/dist/api/core/library/BrowserManager.js +66 -0
  127. package/dist/api/core/library/desktopWorkspace.d.ts +1 -0
  128. package/dist/api/core/library/desktopWorkspace.js +3 -0
  129. package/dist/api/core/library/extensionManager.d.ts +10 -0
  130. package/dist/api/core/library/extensionManager.js +27 -0
  131. package/dist/api/core/library/knowledgeGraph.d.ts +268 -0
  132. package/dist/api/core/library/knowledgeGraph.js +989 -0
  133. package/dist/api/core/library/languageModelTools.d.ts +141 -0
  134. package/dist/api/core/library/languageModelTools.js +207 -0
  135. package/dist/api/core/library/localRuntime.d.ts +23 -0
  136. package/dist/api/core/library/localRuntime.js +144 -0
  137. package/dist/api/core/library/lsp/coreLsp.d.ts +367 -0
  138. package/dist/api/core/library/lsp/coreLsp.js +2076 -0
  139. package/dist/api/core/library/lsp/protocol.d.ts +25 -0
  140. package/dist/api/core/library/lsp/protocol.js +1 -0
  141. package/dist/api/core/library/lsp/serverManager.d.ts +37 -0
  142. package/dist/api/core/library/lsp/serverManager.js +427 -0
  143. package/dist/api/core/library/mcpServerProvider.d.ts +69 -0
  144. package/dist/api/core/library/mcpServerProvider.js +152 -0
  145. package/dist/api/core/library/mcpSettings.d.ts +20 -0
  146. package/dist/api/core/library/mcpSettings.js +131 -0
  147. package/dist/api/core/library/patternMatcher.d.ts +66 -0
  148. package/dist/api/core/library/patternMatcher.js +284 -0
  149. package/dist/api/core/library/regexEscape.d.ts +4 -0
  150. package/dist/api/core/library/regexEscape.js +4 -0
  151. package/dist/api/core/library/runtimeEventBus.d.ts +27 -0
  152. package/dist/api/core/library/runtimeEventBus.js +78 -0
  153. package/dist/api/core/library/safetyMiddleware.d.ts +98 -0
  154. package/dist/api/core/library/safetyMiddleware.js +215 -0
  155. package/dist/api/core/library/tauri.d.ts +71 -0
  156. package/dist/api/core/library/tauri.js +222 -0
  157. package/dist/api/core/library/tauriImport.d.ts +5 -0
  158. package/dist/api/core/library/tauriImport.js +7 -0
  159. package/dist/api/core/library/terminalAutoApproveSettings.d.ts +32 -0
  160. package/dist/api/core/library/terminalAutoApproveSettings.js +529 -0
  161. package/dist/api/core/library/workspaceIdentity.d.ts +10 -0
  162. package/dist/api/core/library/workspaceIdentity.js +49 -0
  163. package/dist/api/core/library/workspaceSummary.d.ts +19 -0
  164. package/dist/api/core/library/workspaceSummary.js +35 -0
  165. package/dist/api/core/skills/agent-customization/SKILL.md +27 -0
  166. package/dist/api/core/skills/bug-fix/SKILL.md +21 -0
  167. package/dist/api/core/skills/create-pr/SKILL.md +32 -0
  168. package/dist/api/core/skills/dev-server/SKILL.md +20 -0
  169. package/dist/api/core/skills/fix-suggestions/SKILL.md +21 -0
  170. package/dist/api/core/skills/github-search/SKILL.md +21 -0
  171. package/dist/api/core/skills/github-summary/SKILL.md +25 -0
  172. package/dist/api/core/skills/integration-tests/SKILL.md +29 -0
  173. package/dist/api/core/skills/pr-comments/SKILL.md +23 -0
  174. package/dist/api/core/skills/project-setup/SKILL.md +20 -0
  175. package/dist/api/core/skills/search-integration/SKILL.md +19 -0
  176. package/dist/api/core/skills/search-results/SKILL.md +19 -0
  177. package/dist/api/core/skills/typescript-upgrade/SKILL.md +21 -0
  178. package/dist/api/data/runStore.d.ts +46 -0
  179. package/dist/api/data/runStore.js +244 -0
  180. package/dist/api/helpers/agentUtils.d.ts +11 -0
  181. package/dist/api/helpers/agentUtils.js +23 -0
  182. package/dist/api/helpers/modelTokenLimits.d.ts +2 -0
  183. package/dist/api/helpers/modelTokenLimits.js +158 -0
  184. package/dist/api/helpers/observationalMemory.d.ts +10 -0
  185. package/dist/api/helpers/observationalMemory.js +36 -0
  186. package/dist/api/helpers/promptBudget.d.ts +30 -0
  187. package/dist/api/helpers/promptBudget.js +216 -0
  188. package/dist/api/helpers/resolveImageMessageParts.d.ts +9 -0
  189. package/dist/api/helpers/resolveImageMessageParts.js +140 -0
  190. package/dist/api/helpers/slashCommands.d.ts +31 -0
  191. package/dist/api/helpers/slashCommands.js +129 -0
  192. package/dist/api/helpers/tokenUsage.d.ts +19 -0
  193. package/dist/api/helpers/tokenUsage.js +81 -0
  194. package/dist/api/routes/fileRoutes.d.ts +2 -0
  195. package/dist/api/routes/fileRoutes.js +108 -0
  196. package/dist/api/routes/lspDocumentRoutes.d.ts +4 -0
  197. package/dist/api/routes/lspDocumentRoutes.js +84 -0
  198. package/dist/api/routes/lspHierarchyRoutes.d.ts +4 -0
  199. package/dist/api/routes/lspHierarchyRoutes.js +122 -0
  200. package/dist/api/routes/lspPositionRoutes.d.ts +4 -0
  201. package/dist/api/routes/lspPositionRoutes.js +263 -0
  202. package/dist/api/routes/lspQueryRoutes.d.ts +4 -0
  203. package/dist/api/routes/lspQueryRoutes.js +679 -0
  204. package/dist/api/routes/lspResolveRoutes.d.ts +4 -0
  205. package/dist/api/routes/lspResolveRoutes.js +143 -0
  206. package/dist/api/routes/lspSemanticDocumentRoutes.d.ts +4 -0
  207. package/dist/api/routes/lspSemanticDocumentRoutes.js +98 -0
  208. package/dist/api/routes/semanticRoutes.d.ts +2 -0
  209. package/dist/api/routes/semanticRoutes.js +126 -0
  210. package/dist/cli.d.ts +8 -0
  211. package/dist/cli.js +160 -0
  212. package/dist/index.d.ts +1 -0
  213. package/dist/index.js +1 -0
  214. package/dist/server.d.ts +2 -0
  215. package/dist/server.js +38 -0
  216. package/package.json +100 -0
@@ -0,0 +1,337 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import { execFile } from "child_process";
4
+ import { promisify } from "util";
5
+ import { z } from "zod";
6
+ import { knowledgeGraph } from "../../library/knowledgeGraph.js";
7
+ import { findFilesViaRepoMap, rankFileCandidates, } from "../utils/repoMapIndex.js";
8
+ import { buildRecoveryQueries } from "../utils/pathRecovery.js";
9
+ const execFileAsync = promisify(execFile);
10
+ const MAX_READ_LINES = 500;
11
+ const normalizeRelativeInputPath = (value) => value.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\//, "");
12
+ const isPathInsideBase = (basePath, targetPath) => {
13
+ const relative = path.relative(basePath, targetPath);
14
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
15
+ };
16
+ const findFileViaTerminal = async (cwd, query) => {
17
+ const normalizedQuery = normalizeRelativeInputPath(query);
18
+ const baseName = path.basename(normalizedQuery);
19
+ const attempts = [];
20
+ if (normalizedQuery && normalizedQuery !== baseName) {
21
+ attempts.push([
22
+ ".",
23
+ "-type",
24
+ "f",
25
+ "-path",
26
+ `./${normalizedQuery}`,
27
+ ]);
28
+ }
29
+ if (baseName) {
30
+ attempts.push([".", "-type", "f", "-name", baseName]);
31
+ }
32
+ for (const args of attempts) {
33
+ try {
34
+ const { stdout } = await execFileAsync("find", args, {
35
+ cwd,
36
+ maxBuffer: 1024 * 1024 * 4,
37
+ });
38
+ const firstMatch = stdout
39
+ .split(/\r?\n/)
40
+ .map((line) => line.trim())
41
+ .filter(Boolean);
42
+ const rankedMatches = await rankFileCandidates(cwd, query, firstMatch.map((match) => path.resolve(cwd, match)));
43
+ const bestMatch = rankedMatches[0];
44
+ if (bestMatch) {
45
+ return bestMatch;
46
+ }
47
+ }
48
+ catch {
49
+ // Ignore and continue to next fallback strategy.
50
+ }
51
+ }
52
+ return null;
53
+ };
54
+ /**
55
+ * Detect language from file extension
56
+ */
57
+ function detectLanguage(filePath) {
58
+ const ext = path.extname(filePath).toLowerCase().replace(/^\./, "");
59
+ if (!ext) {
60
+ return "text";
61
+ }
62
+ switch (ext) {
63
+ case "js":
64
+ return "javascript";
65
+ case "jsx":
66
+ return "jsx";
67
+ case "ts":
68
+ return "typescript";
69
+ case "tsx":
70
+ return "tsx";
71
+ case "py":
72
+ return "python";
73
+ case "java":
74
+ return "java";
75
+ case "go":
76
+ return "go";
77
+ case "cpp":
78
+ return "cpp";
79
+ case "c":
80
+ return "c";
81
+ case "cs":
82
+ return "csharp";
83
+ case "rb":
84
+ return "ruby";
85
+ case "php":
86
+ return "php";
87
+ default:
88
+ return "text";
89
+ }
90
+ }
91
+ /**
92
+ * Tool for reading file contents
93
+ * @param {Object} params - The parameters for reading a file
94
+ * @param {string} params.filePath - The path to the file to read
95
+ * @param {string} [params.encoding='utf8'] - The encoding to use when reading the file
96
+ * @param {boolean} [params.parseWithKnowledgeGraph=true] - Whether to parse file with knowledge graph
97
+ * @returns {Promise<Object>} Object containing success status and file content or error
98
+ */
99
+ export async function readFile({ filePath, workspaceRoot, encoding = "utf8", parseWithKnowledgeGraph = true, startLine, endLine, withLineNumbers, }) {
100
+ try {
101
+ // Validate input
102
+ if (!filePath) {
103
+ return {
104
+ success: false,
105
+ error: "File path is required",
106
+ };
107
+ }
108
+ if ((startLine === undefined) !== (endLine === undefined)) {
109
+ return {
110
+ success: false,
111
+ error: "Both startLine and endLine must be provided together",
112
+ };
113
+ }
114
+ if (startLine !== undefined && endLine !== undefined) {
115
+ const validatedEndLine = endLine;
116
+ if (!Number.isInteger(startLine)
117
+ || !Number.isInteger(validatedEndLine)
118
+ || startLine < 1
119
+ || validatedEndLine < 1) {
120
+ return {
121
+ success: false,
122
+ error: "startLine and endLine must be positive integers",
123
+ };
124
+ }
125
+ }
126
+ if (startLine !== undefined
127
+ && endLine !== undefined
128
+ && startLine > endLine) {
129
+ return {
130
+ success: false,
131
+ error: "startLine cannot be greater than endLine",
132
+ };
133
+ }
134
+ // Prefer the active workspace root when provided so reads keep working
135
+ // after the UI switches folders without changing the server cwd.
136
+ const effectiveWorkspaceRoot = workspaceRoot || process.cwd();
137
+ const absoluteWorkspaceRoot = path.resolve(effectiveWorkspaceRoot);
138
+ // If path is already absolute, use it; otherwise resolve from workspace root.
139
+ // Reads are allowed outside the workspace root when the resolved path is
140
+ // directly readable, so callers can inspect sibling repos and shared files.
141
+ const absolutePath = path.isAbsolute(filePath)
142
+ ? path.resolve(filePath)
143
+ : path.join(effectiveWorkspaceRoot, filePath);
144
+ let readablePath = absolutePath;
145
+ let resolvedBy = "direct";
146
+ // Check if file exists
147
+ try {
148
+ await fs.access(readablePath, fs.constants.R_OK);
149
+ }
150
+ catch {
151
+ const recoveryQueries = buildRecoveryQueries(filePath, absoluteWorkspaceRoot);
152
+ let recoveredPath = null;
153
+ let recoveredByRepoMap = false;
154
+ for (const query of recoveryQueries) {
155
+ const fromRepoMap = await findFilesViaRepoMap(effectiveWorkspaceRoot, query, 1);
156
+ const recoveredPathCandidate = fromRepoMap[0] || (await findFileViaTerminal(effectiveWorkspaceRoot, query));
157
+ const safeRecoveredPath = recoveredPathCandidate
158
+ && isPathInsideBase(absoluteWorkspaceRoot, path.resolve(recoveredPathCandidate))
159
+ ? recoveredPathCandidate
160
+ : null;
161
+ if (safeRecoveredPath) {
162
+ recoveredPath = safeRecoveredPath;
163
+ recoveredByRepoMap = Boolean(fromRepoMap[0]);
164
+ break;
165
+ }
166
+ }
167
+ if (!recoveredPath) {
168
+ return {
169
+ success: false,
170
+ error: `File not found or not readable: ${filePath}`,
171
+ attemptedPath: absolutePath,
172
+ };
173
+ }
174
+ readablePath = recoveredPath;
175
+ resolvedBy = recoveredByRepoMap ? "repo_map" : "terminal_find";
176
+ }
177
+ // Get file stats
178
+ const stats = await fs.stat(readablePath);
179
+ if (!stats.isFile()) {
180
+ return {
181
+ success: false,
182
+ error: `Path is not a file: ${filePath}`,
183
+ };
184
+ }
185
+ // Read file content
186
+ const fullContent = await fs.readFile(readablePath, encoding);
187
+ let content = fullContent;
188
+ let selectedStartLine;
189
+ let selectedEndLine;
190
+ let totalLines;
191
+ const shouldChunkText = encoding === "utf8" || encoding === "ascii";
192
+ if (shouldChunkText) {
193
+ const lines = fullContent.split("\n");
194
+ totalLines = lines.length;
195
+ const requestedStartLine = startLine ?? 1;
196
+ const requestedEndLine = endLine ?? totalLines;
197
+ if (requestedStartLine > totalLines) {
198
+ return {
199
+ success: false,
200
+ error: `Start line ${requestedStartLine} exceeds file length (${totalLines} lines)`,
201
+ };
202
+ }
203
+ selectedStartLine = requestedStartLine;
204
+ selectedEndLine = Math.min(requestedEndLine, selectedStartLine + MAX_READ_LINES - 1, totalLines);
205
+ const selectedLines = lines.slice(selectedStartLine - 1, selectedEndLine);
206
+ const shouldAddLineNumbers = withLineNumbers ?? startLine !== undefined;
207
+ content = shouldAddLineNumbers
208
+ ? selectedLines
209
+ .map((line, index) => `${selectedStartLine + index}:${line}`)
210
+ .join("\n")
211
+ : selectedLines.join("\n");
212
+ }
213
+ // Parse with knowledge graph if enabled and file is code
214
+ let parseResult = null;
215
+ const language = detectLanguage(readablePath);
216
+ const codeLanguages = [
217
+ "javascript",
218
+ "jsx",
219
+ "typescript",
220
+ "tsx",
221
+ "python",
222
+ "java",
223
+ "go",
224
+ ];
225
+ if (parseWithKnowledgeGraph && codeLanguages.includes(language)) {
226
+ try {
227
+ knowledgeGraph.indexFile({
228
+ path: readablePath,
229
+ name: path.basename(readablePath),
230
+ content: fullContent,
231
+ language,
232
+ });
233
+ // Get code summary from knowledge graph
234
+ const node = knowledgeGraph.nodes.get(readablePath);
235
+ if (node) {
236
+ const parsedComponents = Array.from(knowledgeGraph.nodes.values())
237
+ .filter((n) => n.metadata.filePath === readablePath && n.id !== readablePath)
238
+ .map((n) => ({
239
+ type: n.type,
240
+ name: String(n.metadata.name || n.id.split(":").pop() || ""),
241
+ line: n.metadata.startLine,
242
+ }));
243
+ parseResult = {
244
+ parsed: true,
245
+ language,
246
+ components: parsedComponents,
247
+ tokenCount: node.tokenCount,
248
+ relationships: Array.from(node.relationships).map((rel) => ({
249
+ type: rel.type,
250
+ target: rel.nodeId,
251
+ })),
252
+ };
253
+ }
254
+ }
255
+ catch (parseError) {
256
+ const error = parseError;
257
+ console.error("Knowledge graph parsing error:", parseError);
258
+ parseResult = {
259
+ parsed: false,
260
+ error: error.message,
261
+ };
262
+ }
263
+ }
264
+ return {
265
+ success: true,
266
+ filePath: readablePath,
267
+ resolvedBy,
268
+ content,
269
+ ...(selectedStartLine !== undefined
270
+ ? {
271
+ startLine: selectedStartLine,
272
+ endLine: selectedEndLine,
273
+ totalLines,
274
+ hasMore: selectedEndLine < totalLines,
275
+ ...(selectedEndLine < totalLines
276
+ ? {
277
+ nextStartLine: selectedEndLine + 1,
278
+ nextEndLine: Math.min(selectedEndLine + MAX_READ_LINES, totalLines),
279
+ }
280
+ : {}),
281
+ }
282
+ : {}),
283
+ size: stats.size,
284
+ lastModified: stats.mtime,
285
+ knowledgeGraph: parseResult,
286
+ };
287
+ }
288
+ catch (error) {
289
+ const err = error;
290
+ return {
291
+ success: false,
292
+ error: err.message,
293
+ };
294
+ }
295
+ }
296
+ /**
297
+ * Tool metadata for agent system
298
+ */
299
+ export const readFileTool = {
300
+ name: "read_file",
301
+ description: "Read up to 500 lines of a file. Use startLine and endLine with the returned next range to continue reading large files",
302
+ parameters: z.object({
303
+ filePath: z
304
+ .string()
305
+ .describe("The path to the file to read (can be relative or absolute)"),
306
+ workspaceRoot: z
307
+ .string()
308
+ .optional()
309
+ .describe("Workspace root used for path validation and recovery"),
310
+ encoding: z
311
+ .enum(["utf8", "ascii", "base64", "binary", "hex"])
312
+ .default("utf8")
313
+ .describe("The encoding to use when reading the file"),
314
+ startLine: z
315
+ .number()
316
+ .int()
317
+ .positive()
318
+ .optional()
319
+ .describe("First line to return (1-based, inclusive). Must be provided together with endLine"),
320
+ endLine: z
321
+ .number()
322
+ .int()
323
+ .positive()
324
+ .optional()
325
+ .describe("Last line to return (1-based, inclusive). Must be provided together with startLine"),
326
+ withLineNumbers: z
327
+ .boolean()
328
+ .optional()
329
+ .describe("Prefix each returned line with its line number. Defaults to true when a range is requested"),
330
+ parseWithKnowledgeGraph: z
331
+ .boolean()
332
+ .default(true)
333
+ .describe("Whether to automatically parse code files with the knowledge graph for enhanced analysis"),
334
+ }),
335
+ execute: readFile,
336
+ };
337
+ export default readFileTool;
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ type RenameFileParams = {
3
+ oldPath: string;
4
+ newPath: string;
5
+ overwrite?: boolean;
6
+ };
7
+ type RenameFileSuccessResult = {
8
+ success: true;
9
+ oldPath: string;
10
+ newPath: string;
11
+ size: number;
12
+ isMove: boolean;
13
+ message: string;
14
+ };
15
+ type RenameFileErrorResult = {
16
+ success: false;
17
+ error: string;
18
+ };
19
+ type RenameFileResult = RenameFileSuccessResult | RenameFileErrorResult;
20
+ /**
21
+ * Tool for renaming or moving files
22
+ * @param {Object} params - The parameters for renaming a file
23
+ * @param {string} params.oldPath - The current path of the file
24
+ * @param {string} params.newPath - The new path for the file
25
+ * @param {boolean} [params.overwrite=false] - Whether to overwrite if destination exists
26
+ * @returns {Promise<Object>} Object containing success status and rename info or error
27
+ */
28
+ export declare function renameFile({ oldPath, newPath, overwrite, }: RenameFileParams): Promise<RenameFileResult>;
29
+ /**
30
+ * Tool metadata for agent system
31
+ */
32
+ export declare const renameFileTool: {
33
+ description: string;
34
+ parameters: z.ZodObject<{
35
+ oldPath: z.ZodString;
36
+ newPath: z.ZodString;
37
+ overwrite: z.ZodDefault<z.ZodBoolean>;
38
+ }, "strip", z.ZodTypeAny, {
39
+ oldPath?: string;
40
+ newPath?: string;
41
+ overwrite?: boolean;
42
+ }, {
43
+ oldPath?: string;
44
+ newPath?: string;
45
+ overwrite?: boolean;
46
+ }>;
47
+ execute: typeof renameFile;
48
+ };
49
+ export default renameFileTool;
@@ -0,0 +1,86 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import { z } from "zod";
4
+ /**
5
+ * Tool for renaming or moving files
6
+ * @param {Object} params - The parameters for renaming a file
7
+ * @param {string} params.oldPath - The current path of the file
8
+ * @param {string} params.newPath - The new path for the file
9
+ * @param {boolean} [params.overwrite=false] - Whether to overwrite if destination exists
10
+ * @returns {Promise<Object>} Object containing success status and rename info or error
11
+ */
12
+ export async function renameFile({ oldPath, newPath, overwrite = false, }) {
13
+ try {
14
+ // Validate input
15
+ if (!oldPath || !newPath) {
16
+ return {
17
+ success: false,
18
+ error: "Both oldPath and newPath are required",
19
+ };
20
+ }
21
+ // Resolve to absolute paths
22
+ const absoluteOldPath = path.resolve(oldPath);
23
+ const absoluteNewPath = path.resolve(newPath);
24
+ // Check if source file exists
25
+ try {
26
+ await fs.access(absoluteOldPath, fs.constants.F_OK);
27
+ }
28
+ catch (err) {
29
+ return {
30
+ success: false,
31
+ error: `Source file not found: ${oldPath}`,
32
+ };
33
+ }
34
+ // Check if destination exists
35
+ try {
36
+ await fs.access(absoluteNewPath, fs.constants.F_OK);
37
+ if (!overwrite) {
38
+ return {
39
+ success: false,
40
+ error: `Destination already exists: ${newPath}. Set overwrite=true to replace.`,
41
+ };
42
+ }
43
+ }
44
+ catch (err) {
45
+ // Destination doesn't exist, which is fine
46
+ }
47
+ // Create destination directory if it doesn't exist
48
+ const destDir = path.dirname(absoluteNewPath);
49
+ await fs.mkdir(destDir, { recursive: true });
50
+ // Get file stats before moving
51
+ const stats = await fs.stat(absoluteOldPath);
52
+ // Rename/move the file
53
+ await fs.rename(absoluteOldPath, absoluteNewPath);
54
+ return {
55
+ success: true,
56
+ oldPath: absoluteOldPath,
57
+ newPath: absoluteNewPath,
58
+ size: stats.size,
59
+ isMove: path.dirname(absoluteOldPath) !== path.dirname(absoluteNewPath),
60
+ message: "File renamed/moved successfully",
61
+ };
62
+ }
63
+ catch (error) {
64
+ const err = error;
65
+ return {
66
+ success: false,
67
+ error: err.message,
68
+ };
69
+ }
70
+ }
71
+ /**
72
+ * Tool metadata for agent system
73
+ */
74
+ export const renameFileTool = {
75
+ description: "Rename or move a file to a new location",
76
+ parameters: z.object({
77
+ oldPath: z.string().describe("The current path of the file"),
78
+ newPath: z.string().describe("The new path for the file"),
79
+ overwrite: z
80
+ .boolean()
81
+ .default(false)
82
+ .describe("Whether to overwrite if destination exists"),
83
+ }),
84
+ execute: renameFile,
85
+ };
86
+ export default renameFileTool;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Rename Symbol Tool
3
+ * Uses LSP to rename a symbol across the whole workspace.
4
+ */
5
+ type RenameSymbolParams = {
6
+ filePath: string;
7
+ line: number;
8
+ character: number;
9
+ newName: string;
10
+ };
11
+ type FileChange = {
12
+ filePath: string;
13
+ editCount: number;
14
+ edits: Array<{
15
+ startLine: number;
16
+ startCharacter: number;
17
+ endLine: number;
18
+ endCharacter: number;
19
+ newText: string;
20
+ }>;
21
+ };
22
+ type RenameSymbolResult = {
23
+ success: true;
24
+ filePath: string;
25
+ position: {
26
+ line: number;
27
+ character: number;
28
+ };
29
+ newName: string;
30
+ totalFiles: number;
31
+ totalEdits: number;
32
+ files: FileChange[];
33
+ } | {
34
+ success: false;
35
+ message?: string;
36
+ error?: string;
37
+ filePath: string;
38
+ position: {
39
+ line: number;
40
+ character: number;
41
+ };
42
+ };
43
+ declare const renameSymbolTool: {
44
+ name: string;
45
+ description: string;
46
+ parameters: {
47
+ type: string;
48
+ properties: {
49
+ filePath: {
50
+ type: string;
51
+ description: string;
52
+ };
53
+ line: {
54
+ type: string;
55
+ description: string;
56
+ };
57
+ character: {
58
+ type: string;
59
+ description: string;
60
+ };
61
+ newName: {
62
+ type: string;
63
+ description: string;
64
+ };
65
+ };
66
+ required: string[];
67
+ };
68
+ execute({ filePath, line, character, newName }: RenameSymbolParams): Promise<RenameSymbolResult>;
69
+ };
70
+ export default renameSymbolTool;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Rename Symbol Tool
3
+ * Uses LSP to rename a symbol across the whole workspace.
4
+ */
5
+ import { coreLsp } from "../../library/lsp/coreLsp.js";
6
+ import fs from "fs/promises";
7
+ import { fileURLToPath } from "url";
8
+ const renameSymbolTool = {
9
+ name: "renameSymbol",
10
+ description: `Rename a symbol (variable, function, class, etc.) across the entire workspace using LSP.
11
+ Safely renames all references to the symbol at the given position.
12
+
13
+ Use this when you need to:
14
+ - Rename a function, class, or variable consistently everywhere it is used
15
+ - Refactor code with confidence that all call sites are updated
16
+ - Understand the full scope of a rename before applying it`,
17
+ parameters: {
18
+ type: "object",
19
+ properties: {
20
+ filePath: {
21
+ type: "string",
22
+ description: "Absolute path to the file containing the symbol",
23
+ },
24
+ line: {
25
+ type: "number",
26
+ description: "Line number (1-based) of the symbol to rename",
27
+ },
28
+ character: {
29
+ type: "number",
30
+ description: "Character position (1-based) on the line",
31
+ },
32
+ newName: {
33
+ type: "string",
34
+ description: "The new name for the symbol",
35
+ },
36
+ },
37
+ required: ["filePath", "line", "character", "newName"],
38
+ },
39
+ async execute({ filePath, line, character, newName }) {
40
+ try {
41
+ await fs.access(filePath);
42
+ const result = (await coreLsp.renameSymbol(filePath, line, character, newName));
43
+ if (!result) {
44
+ return {
45
+ success: false,
46
+ message: `No rename result for symbol at ${filePath}:${line}:${character}`,
47
+ filePath,
48
+ position: { line, character },
49
+ };
50
+ }
51
+ const files = [];
52
+ const processEdits = (uri, edits) => {
53
+ let filePath;
54
+ try {
55
+ filePath = uri.startsWith("file://") ? fileURLToPath(uri) : uri;
56
+ }
57
+ catch {
58
+ filePath = uri.replace("file://", "");
59
+ }
60
+ files.push({
61
+ filePath,
62
+ editCount: edits.length,
63
+ edits: edits.map((edit) => ({
64
+ startLine: (edit.range?.start?.line ?? 0) + 1,
65
+ startCharacter: (edit.range?.start?.character ?? 0) + 1,
66
+ endLine: (edit.range?.end?.line ?? 0) + 1,
67
+ endCharacter: (edit.range?.end?.character ?? 0) + 1,
68
+ newText: edit.newText ?? newName,
69
+ })),
70
+ });
71
+ };
72
+ if (result.changes) {
73
+ for (const [uri, edits] of Object.entries(result.changes)) {
74
+ processEdits(uri, edits);
75
+ }
76
+ }
77
+ else if (result.documentChanges) {
78
+ for (const change of result.documentChanges) {
79
+ if (change.textDocument?.uri) {
80
+ processEdits(change.textDocument.uri, change.edits ?? []);
81
+ }
82
+ }
83
+ }
84
+ const totalEdits = files.reduce((sum, f) => sum + f.editCount, 0);
85
+ return {
86
+ success: true,
87
+ filePath,
88
+ position: { line, character },
89
+ newName,
90
+ totalFiles: files.length,
91
+ totalEdits,
92
+ files,
93
+ };
94
+ }
95
+ catch (error) {
96
+ const err = error;
97
+ return {
98
+ success: false,
99
+ error: err.message,
100
+ filePath,
101
+ position: { line, character },
102
+ };
103
+ }
104
+ },
105
+ };
106
+ export default renameSymbolTool;