@codebolt/codeboltjs 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/core/messageManager.d.ts +47 -0
  2. package/dist/core/messageManager.js +128 -0
  3. package/dist/{modules → core}/websocket.d.ts +5 -0
  4. package/dist/{modules → core}/websocket.js +15 -10
  5. package/dist/index.d.ts +98 -68
  6. package/dist/index.js +51 -4
  7. package/dist/modules/agent.js +26 -58
  8. package/dist/modules/browser.d.ts +7 -7
  9. package/dist/modules/browser.js +75 -195
  10. package/dist/modules/chat.d.ts +8 -20
  11. package/dist/modules/chat.js +60 -123
  12. package/dist/modules/codeparsers.d.ts +32 -3
  13. package/dist/modules/codeparsers.js +295 -3
  14. package/dist/modules/codeutils.d.ts +24 -7
  15. package/dist/modules/codeutils.js +113 -126
  16. package/dist/modules/crawler.d.ts +1 -16
  17. package/dist/modules/crawler.js +13 -72
  18. package/dist/modules/dbmemory.js +12 -28
  19. package/dist/modules/debug.js +17 -33
  20. package/dist/modules/docutils.d.ts +1 -4
  21. package/dist/modules/docutils.js +52 -2
  22. package/dist/modules/fs.d.ts +47 -1
  23. package/dist/modules/fs.js +151 -162
  24. package/dist/modules/git.d.ts +7 -14
  25. package/dist/modules/git.js +54 -163
  26. package/dist/modules/history.js +11 -27
  27. package/dist/modules/llm.js +8 -16
  28. package/dist/modules/outputparsers.d.ts +36 -4
  29. package/dist/modules/outputparsers.js +56 -5
  30. package/dist/modules/project.d.ts +4 -5
  31. package/dist/modules/project.js +23 -45
  32. package/dist/modules/state.js +29 -69
  33. package/dist/modules/task.js +19 -43
  34. package/dist/modules/terminal.d.ts +3 -2
  35. package/dist/modules/terminal.js +36 -47
  36. package/dist/modules/tokenizer.js +15 -31
  37. package/dist/modules/tools.d.ts +0 -6
  38. package/dist/modules/tools.js +41 -179
  39. package/dist/modules/utils.js +22 -0
  40. package/dist/modules/vectordb.js +30 -62
  41. package/dist/utils/parse-source-code/index.d.ts +9 -0
  42. package/dist/utils/parse-source-code/index.js +233 -0
  43. package/dist/utils/parse-source-code/languageParser.d.ts +8 -0
  44. package/dist/utils/parse-source-code/languageParser.js +137 -0
  45. package/dist/utils/parse-source-code/queries/c-sharp.d.ts +2 -0
  46. package/dist/utils/parse-source-code/queries/c-sharp.js +25 -0
  47. package/dist/utils/parse-source-code/queries/c.d.ts +2 -0
  48. package/dist/utils/parse-source-code/queries/c.js +17 -0
  49. package/dist/utils/parse-source-code/queries/cpp.d.ts +2 -0
  50. package/dist/utils/parse-source-code/queries/cpp.js +25 -0
  51. package/dist/utils/parse-source-code/queries/go.d.ts +2 -0
  52. package/dist/utils/parse-source-code/queries/go.js +29 -0
  53. package/dist/utils/parse-source-code/queries/index.d.ts +12 -0
  54. package/dist/utils/parse-source-code/queries/index.js +30 -0
  55. package/dist/utils/parse-source-code/queries/java.d.ts +2 -0
  56. package/dist/utils/parse-source-code/queries/java.js +17 -0
  57. package/dist/utils/parse-source-code/queries/javascript.d.ts +2 -0
  58. package/dist/utils/parse-source-code/queries/javascript.js +67 -0
  59. package/dist/utils/parse-source-code/queries/php.d.ts +2 -0
  60. package/dist/utils/parse-source-code/queries/php.js +17 -0
  61. package/dist/utils/parse-source-code/queries/python.d.ts +2 -0
  62. package/dist/utils/parse-source-code/queries/python.js +13 -0
  63. package/dist/utils/parse-source-code/queries/ruby.d.ts +2 -0
  64. package/dist/utils/parse-source-code/queries/ruby.js +54 -0
  65. package/dist/utils/parse-source-code/queries/rust.d.ts +2 -0
  66. package/dist/utils/parse-source-code/queries/rust.js +18 -0
  67. package/dist/utils/parse-source-code/queries/swift.d.ts +2 -0
  68. package/dist/utils/parse-source-code/queries/swift.js +47 -0
  69. package/dist/utils/parse-source-code/queries/typescript.d.ts +2 -0
  70. package/dist/utils/parse-source-code/queries/typescript.js +34 -0
  71. package/dist/utils/parse-source-code/tree-sitter-c.wasm +0 -0
  72. package/dist/utils/parse-source-code/tree-sitter-c_sharp.wasm +0 -0
  73. package/dist/utils/parse-source-code/tree-sitter-cpp.wasm +0 -0
  74. package/dist/utils/parse-source-code/tree-sitter-go.wasm +0 -0
  75. package/dist/utils/parse-source-code/tree-sitter-java.wasm +0 -0
  76. package/dist/utils/parse-source-code/tree-sitter-javascript.wasm +0 -0
  77. package/dist/utils/parse-source-code/tree-sitter-php.wasm +0 -0
  78. package/dist/utils/parse-source-code/tree-sitter-python.wasm +0 -0
  79. package/dist/utils/parse-source-code/tree-sitter-ruby.wasm +0 -0
  80. package/dist/utils/parse-source-code/tree-sitter-rust.wasm +0 -0
  81. package/dist/utils/parse-source-code/tree-sitter-swift.wasm +0 -0
  82. package/dist/utils/parse-source-code/tree-sitter-tsx.wasm +0 -0
  83. package/dist/utils/parse-source-code/tree-sitter-typescript.wasm +0 -0
  84. package/dist/utils/parse-source-code/tree-sitter.wasm +0 -0
  85. package/dist/utils.d.ts +1 -1
  86. package/dist/utils.js +1 -1
  87. package/package.json +6 -2
  88. package/dist/utils/editFile.js +0 -30
  89. /package/dist/{utils/editFile.d.ts → modules/utils.d.ts} +0 -0
  90. /package/dist/{modules → utils}/toolBox.d.ts +0 -0
  91. /package/dist/{modules → utils}/toolBox.js +0 -0
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const websocket_1 = __importDefault(require("./websocket"));
6
+ const websocket_1 = __importDefault(require("../core/websocket"));
7
7
  /**
8
8
  * Object containing methods for interacting with Codebolt MCP (Model Context Protocol) tools.
9
9
  * Provides functionality to discover, list, and execute tools.
@@ -15,26 +15,10 @@ const codeboltMCP = {
15
15
  * @returns Promise with the enabled toolboxes data
16
16
  */
17
17
  getEnabledToolBoxes: () => {
18
- return new Promise((resolve, reject) => {
19
- websocket_1.default.getWebsocket.send(JSON.stringify({
20
- "type": "codebolttools",
21
- "action": "getEnabledToolBoxes"
22
- }));
23
- websocket_1.default.getWebsocket.on('message', (data) => {
24
- try {
25
- const response = JSON.parse(data);
26
- if (response.type === "getEnabledToolBoxesResponse") {
27
- resolve(response.data);
28
- }
29
- }
30
- catch (error) {
31
- reject(new Error("Failed to parse response"));
32
- }
33
- });
34
- websocket_1.default.getWebsocket.on('error', (error) => {
35
- reject(error);
36
- });
37
- });
18
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
19
+ "type": "codebolttools",
20
+ "action": "getEnabledToolBoxes"
21
+ }, "getEnabledToolBoxesResponse");
38
22
  },
39
23
  /**
40
24
  * Gets the list of locally available toolboxes.
@@ -42,26 +26,10 @@ const codeboltMCP = {
42
26
  * @returns Promise with the local toolboxes data
43
27
  */
44
28
  getLocalToolBoxes: () => {
45
- return new Promise((resolve, reject) => {
46
- websocket_1.default.getWebsocket.send(JSON.stringify({
47
- "type": "codebolttools",
48
- "action": "getLocalToolBoxes"
49
- }));
50
- websocket_1.default.getWebsocket.on('message', (data) => {
51
- try {
52
- const response = JSON.parse(data);
53
- if (response.type === "getLocalToolBoxesResponse") {
54
- resolve(response.data);
55
- }
56
- }
57
- catch (error) {
58
- reject(new Error("Failed to parse response"));
59
- }
60
- });
61
- websocket_1.default.getWebsocket.on('error', (error) => {
62
- reject(error);
63
- });
64
- });
29
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
30
+ "type": "codebolttools",
31
+ "action": "getLocalToolBoxes"
32
+ }, "getLocalToolBoxesResponse");
65
33
  },
66
34
  /**
67
35
  * Gets toolboxes mentioned in a user message.
@@ -70,36 +38,10 @@ const codeboltMCP = {
70
38
  * @returns Promise with the mentioned toolboxes
71
39
  */
72
40
  getMentionedToolBoxes: (userMessage) => {
73
- return new Promise((resolve, reject) => {
74
- resolve(userMessage.mentionedMCPs);
75
- });
76
- },
77
- /**
78
- * Gets all available toolboxes.
79
- *
80
- * @returns Promise with all available toolboxes data
81
- */
82
- getAvailableToolBoxes: () => {
83
- return new Promise((resolve, reject) => {
84
- websocket_1.default.getWebsocket.send(JSON.stringify({
85
- "type": "codebolttools",
86
- "action": "getAvailableToolBoxes"
87
- }));
88
- websocket_1.default.getWebsocket.on('message', (data) => {
89
- try {
90
- const response = JSON.parse(data);
91
- if (response.type === "getAvailableToolBoxesResponse") {
92
- resolve(response.data);
93
- }
94
- }
95
- catch (error) {
96
- reject(new Error("Failed to parse response"));
97
- }
98
- });
99
- websocket_1.default.getWebsocket.on('error', (error) => {
100
- reject(error);
101
- });
102
- });
41
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
42
+ "type": "codebolttools",
43
+ "action": "getAvailableToolBoxes"
44
+ }, "getAvailableToolBoxesResponse");
103
45
  },
104
46
  /**
105
47
  * Searches for available toolboxes matching a query.
@@ -108,27 +50,11 @@ const codeboltMCP = {
108
50
  * @returns Promise with matching toolboxes data
109
51
  */
110
52
  searchAvailableToolBoxes: (query) => {
111
- return new Promise((resolve, reject) => {
112
- websocket_1.default.getWebsocket.send(JSON.stringify({
113
- "type": "codebolttools",
114
- "action": "searchAvailableToolBoxes",
115
- "query": query
116
- }));
117
- websocket_1.default.getWebsocket.on('message', (data) => {
118
- try {
119
- const response = JSON.parse(data);
120
- if (response.type === "searchAvailableToolBoxesResponse") {
121
- resolve(response.data);
122
- }
123
- }
124
- catch (error) {
125
- reject(new Error("Failed to parse response"));
126
- }
127
- });
128
- websocket_1.default.getWebsocket.on('error', (error) => {
129
- reject(error);
130
- });
131
- });
53
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
54
+ "type": "codebolttools",
55
+ "action": "searchAvailableToolBoxes",
56
+ "query": query
57
+ }, "searchAvailableToolBoxesResponse");
132
58
  },
133
59
  /**
134
60
  * Lists all tools from the specified toolboxes.
@@ -137,27 +63,11 @@ const codeboltMCP = {
137
63
  * @returns Promise with tools from the specified toolboxes
138
64
  */
139
65
  listToolsFromToolBoxes: (toolBoxes) => {
140
- return new Promise((resolve, reject) => {
141
- websocket_1.default.getWebsocket.send(JSON.stringify({
142
- "type": "codebolttools",
143
- "action": "listToolsFromToolBoxes",
144
- "toolBoxes": toolBoxes
145
- }));
146
- websocket_1.default.getWebsocket.on('message', (data) => {
147
- try {
148
- const response = JSON.parse(data);
149
- if (response.type === "listToolsFromToolBoxesResponse") {
150
- resolve(response.data);
151
- }
152
- }
153
- catch (error) {
154
- reject(new Error("Failed to parse response"));
155
- }
156
- });
157
- websocket_1.default.getWebsocket.on('error', (error) => {
158
- reject(error);
159
- });
160
- });
66
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
67
+ "type": "codebolttools",
68
+ "action": "listToolsFromToolBoxes",
69
+ "toolBoxes": toolBoxes
70
+ }, "listToolsFromToolBoxesResponse");
161
71
  },
162
72
  /**
163
73
  * Configures a specific toolbox with provided configuration.
@@ -167,28 +77,12 @@ const codeboltMCP = {
167
77
  * @returns Promise with the configuration result
168
78
  */
169
79
  configureToolBox: (name, config) => {
170
- return new Promise((resolve, reject) => {
171
- websocket_1.default.getWebsocket.send(JSON.stringify({
172
- "type": "codebolttools",
173
- "action": "configureToolBox",
174
- "mcpName": name,
175
- "config": config
176
- }));
177
- websocket_1.default.getWebsocket.on('message', (data) => {
178
- try {
179
- const response = JSON.parse(data);
180
- if (response.type === "configureToolBoxResponse") {
181
- resolve(response.data);
182
- }
183
- }
184
- catch (error) {
185
- reject(new Error("Failed to parse response"));
186
- }
187
- });
188
- websocket_1.default.getWebsocket.on('error', (error) => {
189
- reject(error);
190
- });
191
- });
80
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
81
+ "type": "codebolttools",
82
+ "action": "configureToolBox",
83
+ "mcpName": name,
84
+ "config": config
85
+ }, "configureToolBoxResponse");
192
86
  },
193
87
  /**
194
88
  * Gets detailed information about specific tools.
@@ -197,27 +91,11 @@ const codeboltMCP = {
197
91
  * @returns Promise with detailed information about the tools
198
92
  */
199
93
  getTools: (tools) => {
200
- return new Promise((resolve, reject) => {
201
- websocket_1.default.getWebsocket.send(JSON.stringify({
202
- "type": "codebolttools",
203
- "action": "getTools",
204
- "toolboxes": tools
205
- }));
206
- websocket_1.default.getWebsocket.on('message', (data) => {
207
- try {
208
- const response = JSON.parse(data);
209
- if (response.type === "getToolsResponse") {
210
- resolve(response.data);
211
- }
212
- }
213
- catch (error) {
214
- reject(new Error("Failed to parse response"));
215
- }
216
- });
217
- websocket_1.default.getWebsocket.on('error', (error) => {
218
- reject(error);
219
- });
220
- });
94
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
95
+ "type": "codebolttools",
96
+ "action": "getTools",
97
+ "toolboxes": tools
98
+ }, "getToolsResponse");
221
99
  },
222
100
  /**
223
101
  * Executes a specific tool with provided parameters.
@@ -228,28 +106,12 @@ const codeboltMCP = {
228
106
  * @returns Promise with the execution result
229
107
  */
230
108
  executeTool: (toolbox, toolName, params) => {
231
- return new Promise((resolve, reject) => {
232
- websocket_1.default.getWebsocket.send(JSON.stringify({
233
- "type": "codebolttools",
234
- "action": "executeTool",
235
- "toolName": `${toolbox}--${toolName}`,
236
- "params": params
237
- }));
238
- websocket_1.default.getWebsocket.on('message', (data) => {
239
- try {
240
- const response = JSON.parse(data);
241
- if (response.type === "executeToolResponse") {
242
- resolve(response.data);
243
- }
244
- }
245
- catch (error) {
246
- reject(new Error("Failed to parse response"));
247
- }
248
- });
249
- websocket_1.default.getWebsocket.on('error', (error) => {
250
- reject(error);
251
- });
252
- });
253
- },
109
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
110
+ "type": "codebolttools",
111
+ "action": "executeTool",
112
+ "toolName": `${toolbox}--${toolName}`,
113
+ "params": params
114
+ }, "executeToolResponse");
115
+ }
254
116
  };
255
117
  exports.default = codeboltMCP;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const websocket_1 = __importDefault(require("../core/websocket"));
7
+ const cbutils = {
8
+ editFileAndApplyDiff: (filePath, diff, diffIdentifier, prompt, applyModel) => {
9
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
10
+ "type": "fsEvent",
11
+ "action": "editFileAndApplyDiff",
12
+ message: {
13
+ filePath,
14
+ diff,
15
+ diffIdentifier,
16
+ prompt,
17
+ applyModel
18
+ }
19
+ }, "editFileAndApplyDiffResponse");
20
+ }
21
+ };
22
+ exports.default = cbutils;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const websocket_1 = __importDefault(require("./websocket"));
6
+ const websocket_1 = __importDefault(require("../core/websocket"));
7
7
  const VectorDB = {
8
8
  /**
9
9
  * Retrieves a vector from the vector database based on the provided key.
@@ -12,21 +12,13 @@ const VectorDB = {
12
12
  * @returns {Promise<GetVectorResponse>} A promise that resolves with the retrieved vector.
13
13
  */
14
14
  getVector: async (key) => {
15
- return new Promise((resolve, reject) => {
16
- websocket_1.default.getWebsocket.send(JSON.stringify({
17
- "type": "vectordbEvent",
18
- "action": "getVector",
19
- "message": {
20
- item: key
21
- },
22
- }));
23
- websocket_1.default.getWebsocket.on('message', (data) => {
24
- const response = JSON.parse(data);
25
- if (response.type === "getVectorResponse") {
26
- resolve(response);
27
- }
28
- });
29
- });
15
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
16
+ "type": "vectordbEvent",
17
+ "action": "getVector",
18
+ "message": {
19
+ item: key
20
+ },
21
+ }, "getVectorResponse");
30
22
  },
31
23
  /**
32
24
  * Adds a new vector item to the vector database.
@@ -36,21 +28,13 @@ const VectorDB = {
36
28
  * @returns {Promise<AddVectorItemResponse>} A promise that resolves when the item is successfully added.
37
29
  */
38
30
  addVectorItem: async (item) => {
39
- return new Promise((resolve, reject) => {
40
- websocket_1.default.getWebsocket.send(JSON.stringify({
41
- "type": "vectordbEvent",
42
- "action": "addVectorItem",
43
- "message": {
44
- item: item
45
- },
46
- }));
47
- websocket_1.default.getWebsocket.on('message', (data) => {
48
- const response = JSON.parse(data);
49
- if (response.type === "addVectorItemResponse") {
50
- resolve(response);
51
- }
52
- });
53
- });
31
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
32
+ "type": "vectordbEvent",
33
+ "action": "addVectorItem",
34
+ "message": {
35
+ item: item
36
+ },
37
+ }, "addVectorItemResponse");
54
38
  },
55
39
  /**
56
40
  * Queries a vector item from the vector database based on the provided key.
@@ -59,21 +43,13 @@ const VectorDB = {
59
43
  * @returns {Promise<QueryVectorItemResponse>} A promise that resolves with the queried vector item.
60
44
  */
61
45
  queryVectorItem: async (key) => {
62
- return new Promise((resolve, reject) => {
63
- websocket_1.default.getWebsocket.send(JSON.stringify({
64
- "type": "vectordbEvent",
65
- "action": "queryVectorItem",
66
- "message": {
67
- item: key
68
- },
69
- }));
70
- websocket_1.default.getWebsocket.on('message', (data) => {
71
- const response = JSON.parse(data);
72
- if (response.type === "qeryVectorItemResponse") {
73
- resolve(response);
74
- }
75
- });
76
- });
46
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
47
+ "type": "vectordbEvent",
48
+ "action": "queryVectorItem",
49
+ "message": {
50
+ item: key
51
+ },
52
+ }, "qeryVectorItemResponse");
77
53
  },
78
54
  /**
79
55
  * Queries a vector item from the vector database based on the provided key.
@@ -82,22 +58,14 @@ const VectorDB = {
82
58
  * @returns {Promise<QueryVectorItemResponse>} A promise that resolves with the queried vector item.
83
59
  */
84
60
  queryVectorItems: async (items, dbPath) => {
85
- return new Promise((resolve, reject) => {
86
- websocket_1.default.getWebsocket.send(JSON.stringify({
87
- "type": "vectordbEvent",
88
- "action": "queryVectorItems",
89
- "message": {
90
- items,
91
- dbPath
92
- },
93
- }));
94
- websocket_1.default.getWebsocket.on('message', (data) => {
95
- const response = JSON.parse(data);
96
- if (response.type === "qeryVectorItemsResponse") {
97
- resolve(response);
98
- }
99
- });
100
- });
61
+ return websocket_1.default.messageManager.sendAndWaitForResponse({
62
+ "type": "vectordbEvent",
63
+ "action": "queryVectorItems",
64
+ "message": {
65
+ items,
66
+ dbPath
67
+ },
68
+ }, "qeryVectorItemsResponse");
101
69
  },
102
70
  };
103
71
  exports.default = VectorDB;
@@ -0,0 +1,9 @@
1
+ export declare const LIST_FILES_LIMIT = 200;
2
+ export declare function parseSourceCodeForDefinitionsTopLevel(dirPath: string): Promise<string>;
3
+ export declare function listFiles(dirPath: string, recursive: boolean): Promise<string[]>;
4
+ export declare function globbyLevelByLevel(options: any): Promise<string[]>;
5
+ export declare function separateFiles(allFiles: string[]): {
6
+ filesToParse: string[];
7
+ remainingFiles: string[];
8
+ };
9
+ export declare function parseFile(filePath: string, languageParsers: any): Promise<string | undefined>;
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseFile = exports.separateFiles = exports.globbyLevelByLevel = exports.listFiles = exports.parseSourceCodeForDefinitionsTopLevel = exports.LIST_FILES_LIMIT = void 0;
4
+ const fs = require("fs/promises");
5
+ const os = require("os");
6
+ const path = require("path");
7
+ const { LanguageParser, loadRequiredLanguageParsers } = require("./languageParser");
8
+ // const { globby } = await import("globby");
9
+ const { globby } = require('@codebolt/globby');
10
+ exports.LIST_FILES_LIMIT = 200;
11
+ // TODO: implement caching behavior to avoid having to keep analyzing project for new tasks.
12
+ async function parseSourceCodeForDefinitionsTopLevel(dirPath) {
13
+ // check if the path exists
14
+ const dirExists = await fs
15
+ .access(path.resolve(dirPath))
16
+ .then(() => true)
17
+ .catch(() => false);
18
+ if (!dirExists) {
19
+ return "This directory does not exist or you do not have permission to access it.";
20
+ }
21
+ // Get all files at top level (not gitignored)
22
+ const allFiles = await listFiles(dirPath, false);
23
+ let result = "";
24
+ // Separate files to parse and remaining files
25
+ const { filesToParse, remainingFiles } = separateFiles(allFiles);
26
+ const languageParsers = await loadRequiredLanguageParsers(filesToParse);
27
+ // Parse specific files we have language parsers for
28
+ // const filesWithoutDefinitions: string[] = []
29
+ for (const file of filesToParse) {
30
+ const definitions = await parseFile(file, languageParsers);
31
+ if (definitions) {
32
+ result += `${path.relative(dirPath, file)}\n${definitions}\n`;
33
+ }
34
+ // else {
35
+ // filesWithoutDefinitions.push(file)
36
+ // }
37
+ }
38
+ // List remaining files' paths
39
+ // let didFindUnparsedFiles = false
40
+ // filesWithoutDefinitions
41
+ // .concat(remainingFiles)
42
+ // .sort()
43
+ // .forEach((file) => {
44
+ // if (!didFindUnparsedFiles) {
45
+ // result += "# Unparsed Files\n\n"
46
+ // didFindUnparsedFiles = true
47
+ // }
48
+ // result += `${path.relative(dirPath, file)}\n`
49
+ // })
50
+ return result ? result : "No source code definitions found.";
51
+ }
52
+ exports.parseSourceCodeForDefinitionsTopLevel = parseSourceCodeForDefinitionsTopLevel;
53
+ async function listFiles(dirPath, recursive) {
54
+ const absolutePath = path.resolve(dirPath);
55
+ // Do not allow listing files in root or home directory, which Claude tends to want to do when the user's prompt is vague.
56
+ const root = process.platform === "win32" ? path.parse(absolutePath).root : "/";
57
+ const isRoot = absolutePath === root;
58
+ if (isRoot) {
59
+ return [root];
60
+ }
61
+ const homeDir = os.homedir();
62
+ const isHomeDir = absolutePath === homeDir;
63
+ if (isHomeDir) {
64
+ return [homeDir];
65
+ }
66
+ const dirsToIgnore = [
67
+ "node_modules",
68
+ "__pycache__",
69
+ "env",
70
+ "venv",
71
+ "target/dependency",
72
+ "build/dependencies",
73
+ "dist",
74
+ "out",
75
+ "bundle",
76
+ "vendor",
77
+ "tmp",
78
+ "temp",
79
+ "deps",
80
+ "pkg",
81
+ "Pods",
82
+ ".*", // Ignore all hidden directories and files
83
+ ].map((dir) => `**/${dir}/**`);
84
+ const options = {
85
+ cwd: dirPath,
86
+ dot: false, // do not ignore hidden files/directories
87
+ absolute: true,
88
+ markDirectories: true, // Append a / on any directories matched
89
+ gitignore: recursive, // globby ignores any files that are gitignored
90
+ ignore: recursive ? dirsToIgnore : undefined, // just in case there is no gitignore, we ignore sensible defaults
91
+ onlyFiles: false, // true by default, false means it will list directories on their own too
92
+ };
93
+ // * globs all files in one dir, ** globs files in nested directories
94
+ const files = recursive
95
+ ? await globbyLevelByLevel(options)
96
+ : (await globby("*", options)).slice(0, exports.LIST_FILES_LIMIT);
97
+ return files;
98
+ }
99
+ exports.listFiles = listFiles;
100
+ // globby doesnt natively support top down level by level globbing, so we implement it ourselves
101
+ async function globbyLevelByLevel(options) {
102
+ let results = [];
103
+ const globbingProcess = async () => {
104
+ let currentLevel = 0;
105
+ // const { globby } = await import("globby");
106
+ while (results.length < exports.LIST_FILES_LIMIT) {
107
+ const pattern = currentLevel === 0 ? "*" : `${"*/".repeat(currentLevel)}*`;
108
+ const filesAtLevel = await globby(pattern, options);
109
+ if (filesAtLevel.length === 0) {
110
+ break;
111
+ }
112
+ results.push(...filesAtLevel);
113
+ if (results.length >= exports.LIST_FILES_LIMIT) {
114
+ results = results.slice(0, exports.LIST_FILES_LIMIT);
115
+ break;
116
+ }
117
+ currentLevel++;
118
+ }
119
+ return results;
120
+ };
121
+ // Timeout after 10 seconds and return partial results
122
+ const timeoutPromise = new Promise((_, reject) => {
123
+ setTimeout(() => reject(new Error("Globbing timeout")), 10000);
124
+ });
125
+ try {
126
+ return await Promise.race([globbingProcess(), timeoutPromise]);
127
+ }
128
+ catch (error) {
129
+ console.log(error);
130
+ console.warn("Globbing timed out, returning partial results");
131
+ return results;
132
+ }
133
+ }
134
+ exports.globbyLevelByLevel = globbyLevelByLevel;
135
+ function separateFiles(allFiles) {
136
+ const extensions = [
137
+ "js",
138
+ "jsx",
139
+ "ts",
140
+ "tsx",
141
+ "py",
142
+ // Rust
143
+ "rs",
144
+ "go",
145
+ // C
146
+ "c",
147
+ "h",
148
+ // C++
149
+ "cpp",
150
+ "hpp",
151
+ // C#
152
+ "cs",
153
+ // Ruby
154
+ "rb",
155
+ "java",
156
+ "php",
157
+ "swift",
158
+ ].map((e) => `.${e}`);
159
+ const filesToParse = allFiles.filter((file) => extensions.includes(path.extname(file))).slice(0, 50); // 50 files max
160
+ const remainingFiles = allFiles.filter((file) => !filesToParse.includes(file));
161
+ return { filesToParse, remainingFiles };
162
+ }
163
+ exports.separateFiles = separateFiles;
164
+ /*
165
+ Parsing files using tree-sitter
166
+
167
+ 1. Parse the file content into an AST (Abstract Syntax Tree) using the appropriate language grammar (set of rules that define how the components of a language like keywords, expressions, and statements can be combined to create valid programs).
168
+ 2. Create a query using a language-specific query string, and run it against the AST's root node to capture specific syntax elements.
169
+ - We use tag queries to identify named entities in a program, and then use a syntax capture to label the entity and its name. A notable example of this is GitHub's search-based code navigation.
170
+ - Our custom tag queries are based on tree-sitter's default tag queries, but modified to only capture definitions.
171
+ 3. Sort the captures by their position in the file, output the name of the definition, and format by i.e. adding "|----\n" for gaps between captured sections.
172
+
173
+ This approach allows us to focus on the most relevant parts of the code (defined by our language-specific queries) and provides a concise yet informative view of the file's structure and key elements.
174
+
175
+ - https://github.com/tree-sitter/node-tree-sitter/blob/master/test/query_test.js
176
+ - https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/test/query-test.js
177
+ - https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/test/helper.js
178
+ - https://tree-sitter.github.io/tree-sitter/code-navigation-systems
179
+ */
180
+ async function parseFile(filePath, languageParsers) {
181
+ const fileContent = await fs.readFile(filePath, "utf8");
182
+ const ext = path.extname(filePath).toLowerCase().slice(1);
183
+ const { parser, query } = languageParsers[ext] || {};
184
+ if (!parser || !query) {
185
+ return `Unsupported file type: ${filePath}`;
186
+ }
187
+ let formattedOutput = "";
188
+ try {
189
+ // Parse the file content into an Abstract Syntax Tree (AST), a tree-like representation of the code
190
+ const tree = parser.parse(fileContent);
191
+ // Apply the query to the AST and get the captures
192
+ // Captures are specific parts of the AST that match our query patterns, each capture represents a node in the AST that we're interested in.
193
+ const captures = query.captures(tree.rootNode);
194
+ // Sort captures by their start position
195
+ captures.sort((a, b) => a.node.startPosition.row - b.node.startPosition.row);
196
+ // Split the file content into individual lines
197
+ const lines = fileContent.split("\n");
198
+ // Keep track of the last line we've processed
199
+ let lastLine = -1;
200
+ captures.forEach((capture) => {
201
+ const { node, name } = capture;
202
+ // Get the start and end lines of the current AST node
203
+ const startLine = node.startPosition.row;
204
+ const endLine = node.endPosition.row;
205
+ // Once we've retrieved the nodes we care about through the language query, we filter for lines with definition names only.
206
+ // name.startsWith("name.reference.") > refs can be used for ranking purposes, but we don't need them for the output
207
+ // previously we did `name.startsWith("name.definition.")` but this was too strict and excluded some relevant definitions
208
+ // Add separator if there's a gap between captures
209
+ if (lastLine !== -1 && startLine > lastLine + 1) {
210
+ formattedOutput += "|----\n";
211
+ }
212
+ // Only add the first line of the definition
213
+ // query captures includes the definition name and the definition implementation, but we only want the name (I found discrepencies in the naming structure for various languages, i.e. javascript names would be 'name' and typescript names would be 'name.definition)
214
+ if (name.includes("name") && lines[startLine]) {
215
+ formattedOutput += `│${lines[startLine]}\n`;
216
+ }
217
+ // Adds all the captured lines
218
+ // for (let i = startLine; i <= endLine; i++) {
219
+ // formattedOutput += `│${lines[i]}\n`
220
+ // }
221
+ //}
222
+ lastLine = endLine;
223
+ });
224
+ }
225
+ catch (error) {
226
+ console.log(`Error parsing file: ${error}\n`);
227
+ }
228
+ if (formattedOutput.length > 0) {
229
+ return `|----\n${formattedOutput}|----\n`;
230
+ }
231
+ return undefined;
232
+ }
233
+ exports.parseFile = parseFile;