devbonzai 2.1.2 → 2.1.3

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.
@@ -1,208 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { listAllFiles } = require('../utils/file-list');
4
- const { extractPythonFunctions, extractJavaScriptFunctions, extractVueFunctions } = require('../utils/parsers');
5
-
6
- function setupCrudRoutes(app, ROOT) {
7
- // GET /list - List all files
8
- app.get('/list', (req, res) => {
9
- try {
10
- const rootName = path.basename(ROOT);
11
- const files = listAllFiles(ROOT, rootName);
12
- res.json({ files });
13
- } catch (e) {
14
- res.status(500).send(e.message);
15
- }
16
- });
17
-
18
- // GET /read - Read file content
19
- app.get('/read', (req, res) => {
20
- try {
21
- const requestedPath = req.query.path || '';
22
- const filePath = path.join(ROOT, requestedPath);
23
-
24
- if (!filePath.startsWith(ROOT)) {
25
- return res.status(400).send('Invalid path');
26
- }
27
-
28
- // Helper function to find and return content from parse result
29
- const findAndReturn = (parseResult, name, type) => {
30
- if (type === 'function') {
31
- const target = parseResult.functions.find(f => f.name === name);
32
- if (target) return target.content;
33
- } else if (type === 'method') {
34
- // Method name format: ClassName.methodName
35
- for (const cls of parseResult.classes) {
36
- const method = cls.methods.find(m => m.name === name);
37
- if (method) return method.content;
38
- }
39
- } else if (type === 'class') {
40
- const target = parseResult.classes.find(c => c.name === name);
41
- if (target) return target.content;
42
- }
43
- return null;
44
- };
45
-
46
- // Check if this is a virtual file request (.function, .method, or .class)
47
- if (requestedPath.endsWith('.function') || requestedPath.endsWith('.method') || requestedPath.endsWith('.class')) {
48
- // Traverse up the path to find the actual source file
49
- let currentPath = filePath;
50
- let sourceFilePath = null;
51
- let parser = null;
52
-
53
- // Keep going up until we find a source file (.py, .js, .jsx, .ts, .tsx, .vue)
54
- while (currentPath !== ROOT && currentPath !== path.dirname(currentPath)) {
55
- const stat = fs.existsSync(currentPath) ? fs.statSync(currentPath) : null;
56
-
57
- // Check if current path is a file with a supported extension
58
- if (stat && stat.isFile()) {
59
- if (currentPath.endsWith('.py')) {
60
- parser = extractPythonFunctions;
61
- sourceFilePath = currentPath;
62
- break;
63
- } else if (currentPath.endsWith('.js') || currentPath.endsWith('.jsx') ||
64
- currentPath.endsWith('.ts') || currentPath.endsWith('.tsx')) {
65
- parser = extractJavaScriptFunctions;
66
- sourceFilePath = currentPath;
67
- break;
68
- } else if (currentPath.endsWith('.vue')) {
69
- parser = extractVueFunctions;
70
- sourceFilePath = currentPath;
71
- break;
72
- }
73
- }
74
-
75
- // Move up one level
76
- const parentPath = path.dirname(currentPath);
77
- if (parentPath === currentPath) break; // Reached root
78
- currentPath = parentPath;
79
- }
80
-
81
- if (!sourceFilePath || !parser) {
82
- return res.status(404).send('Source file not found for virtual file');
83
- }
84
-
85
- // Extract the requested item name from the requested path
86
- let itemName = '';
87
- let itemType = '';
88
-
89
- if (requestedPath.endsWith('.function')) {
90
- itemName = path.basename(requestedPath, '.function');
91
- itemType = 'function';
92
- } else if (requestedPath.endsWith('.method')) {
93
- itemName = path.basename(requestedPath, '.method');
94
- itemType = 'method';
95
- } else if (requestedPath.endsWith('.class')) {
96
- itemName = path.basename(requestedPath, '.class');
97
- itemType = 'class';
98
- }
99
-
100
- // Check if the source file exists
101
- try {
102
- if (!fs.existsSync(sourceFilePath)) {
103
- return res.status(404).send('Source file not found');
104
- }
105
-
106
- // Parse the file
107
- const parseResult = parser(sourceFilePath);
108
-
109
- // Find and return the content
110
- const content = findAndReturn(parseResult, itemName, itemType);
111
-
112
- if (!content) {
113
- return res.status(404).send(`${itemType} '${itemName}' not found in file`);
114
- }
115
-
116
- return res.json({ content });
117
- } catch (e) {
118
- const errorType = requestedPath.endsWith('.function') ? 'function' :
119
- requestedPath.endsWith('.method') ? 'method' : 'class';
120
- return res.status(500).send('Error reading ' + errorType + ': ' + e.message);
121
- }
122
- }
123
-
124
- // Regular file read
125
- const content = fs.readFileSync(filePath, 'utf8');
126
- res.json({ content });
127
- } catch (e) {
128
- res.status(500).send(e.message);
129
- }
130
- });
131
-
132
- // POST /write - Write file content
133
- app.post('/write', (req, res) => {
134
- try {
135
- const filePath = path.join(ROOT, req.body.path || '');
136
- if (!filePath.startsWith(ROOT)) {
137
- return res.status(400).send('Invalid path');
138
- }
139
- fs.writeFileSync(filePath, req.body.content, 'utf8');
140
- res.json({ status: 'ok' });
141
- } catch (e) {
142
- res.status(500).send(e.message);
143
- }
144
- });
145
-
146
- // POST /write_dir - Create directory
147
- app.post('/write_dir', (req, res) => {
148
- try {
149
- const dirPath = path.join(ROOT, req.body.path || '');
150
- if (!dirPath.startsWith(ROOT)) {
151
- return res.status(400).send('Invalid path');
152
- }
153
- // Create directory recursively (creates parent directories if they don't exist)
154
- fs.mkdirSync(dirPath, { recursive: true });
155
- res.json({ status: 'ok' });
156
- } catch (e) {
157
- res.status(500).send(e.message);
158
- }
159
- });
160
-
161
- // POST /delete - Delete file or directory
162
- app.post('/delete', (req, res) => {
163
- try {
164
- const targetPath = path.join(ROOT, req.body.path || '');
165
- if (!targetPath.startsWith(ROOT)) {
166
- return res.status(400).send('Invalid path');
167
- }
168
- // Delete file or directory recursively
169
- fs.rmSync(targetPath, { recursive: true, force: true });
170
- res.json({ status: 'ok' });
171
- } catch (e) {
172
- res.status(500).send(e.message);
173
- }
174
- });
175
-
176
- // POST /move - Move file or folder
177
- app.post('/move', (req, res) => {
178
- try {
179
- const sourcePath = path.join(ROOT, req.body.source || '');
180
- const destinationPath = path.join(ROOT, req.body.destination || '');
181
-
182
- // Validate both paths are within ROOT directory
183
- if (!sourcePath.startsWith(ROOT) || !destinationPath.startsWith(ROOT)) {
184
- return res.status(400).send('Invalid path');
185
- }
186
-
187
- // Check if source exists
188
- if (!fs.existsSync(sourcePath)) {
189
- return res.status(400).send('Source path does not exist');
190
- }
191
-
192
- // Ensure destination directory exists
193
- const destinationDir = path.dirname(destinationPath);
194
- if (!fs.existsSync(destinationDir)) {
195
- fs.mkdirSync(destinationDir, { recursive: true });
196
- }
197
-
198
- // Move the file or folder
199
- fs.renameSync(sourcePath, destinationPath);
200
- res.json({ status: 'ok' });
201
- } catch (e) {
202
- res.status(500).send(e.message);
203
- }
204
- });
205
- }
206
-
207
- module.exports = { setupCrudRoutes };
208
-
@@ -1,147 +0,0 @@
1
- const path = require('path');
2
- const { exec } = require('child_process');
3
-
4
- function setupInfraRoutes(app, ROOT) {
5
- // GET / - Root route with API documentation
6
- app.get('/', (req, res) => {
7
- res.json({
8
- message: 'Local File Server API',
9
- endpoints: {
10
- 'GET /list': 'List all files in the directory',
11
- 'GET /read?path=<filepath>': 'Read file content',
12
- 'POST /write': 'Write file content (body: {path, content})',
13
- 'POST /write_dir': 'Create directory (body: {path})',
14
- 'POST /delete': 'Delete file or directory (body: {path})',
15
- 'POST /move': 'Move file or folder (body: {source, destination})',
16
- 'POST /open-cursor': 'Open Cursor (body: {path, line?})',
17
- 'POST /analyze_prompt': 'Analyze what files would be modified (body: {prompt})',
18
- 'POST /prompt_agent': 'Execute cursor-agent command (body: {prompt})',
19
- 'POST /prompt_agent_stream': 'Execute cursor-agent with SSE streaming (body: {prompt})',
20
- 'POST /revert_job': 'Revert to a previous commit (body: {beforeCommit})',
21
- 'POST /shutdown': 'Gracefully shutdown the server'
22
- },
23
- example: 'Try: /list or /read?path=README.md'
24
- });
25
- });
26
-
27
- // POST /open-cursor - Open file in Cursor IDE
28
- app.post('/open-cursor', (req, res) => {
29
- try {
30
- const requestedPath = req.body.path || '';
31
-
32
- // Resolve path relative to ROOT (similar to other endpoints)
33
- // If path is absolute and within ROOT, use it directly
34
- // Otherwise, resolve it relative to ROOT
35
- let filePath;
36
- if (path.isAbsolute(requestedPath)) {
37
- // If absolute path, check if it's within ROOT
38
- if (requestedPath.startsWith(ROOT)) {
39
- filePath = requestedPath;
40
- } else {
41
- // Path might contain incorrect segments (like "codemaps")
42
- // Try to find ROOT in the path and extract the relative part
43
- const rootIndex = requestedPath.indexOf(ROOT);
44
- if (rootIndex !== -1) {
45
- // Extract the part after ROOT and remove leading slashes
46
- let relativePart = requestedPath.substring(rootIndex + ROOT.length);
47
- while (relativePart.startsWith('/')) {
48
- relativePart = relativePart.substring(1);
49
- }
50
- filePath = path.join(ROOT, relativePart);
51
- } else {
52
- return res.status(400).json({ error: 'Invalid path: path must be within project root' });
53
- }
54
- }
55
- } else {
56
- // Relative path - resolve relative to ROOT
57
- // Remove root directory name prefix if present (from /list endpoint format)
58
- const rootName = path.basename(ROOT);
59
- let relativePath = requestedPath;
60
- if (relativePath.startsWith(rootName + '/')) {
61
- relativePath = relativePath.substring(rootName.length + 1);
62
- }
63
- filePath = path.join(ROOT, relativePath);
64
- }
65
-
66
- // Validate the resolved path is within ROOT
67
- if (!filePath.startsWith(ROOT)) {
68
- return res.status(400).json({ error: 'Invalid path' });
69
- }
70
-
71
- const { line } = req.body;
72
-
73
- // Always use cursor CLI command first (it handles line numbers correctly)
74
- const cursorCommands = [
75
- 'cursor',
76
- '/Applications/Cursor.app/Contents/Resources/app/bin/cursor',
77
- '/usr/local/bin/cursor',
78
- 'code'
79
- ];
80
-
81
- const tryCommand = (commandIndex = 0) => {
82
- if (commandIndex >= cursorCommands.length) {
83
- return res.status(500).json({
84
- error: 'Cursor not found. Please install Cursor CLI or check Cursor installation.'
85
- });
86
- }
87
-
88
- // Use proper Cursor CLI syntax for line numbers
89
- const command = line
90
- ? `${cursorCommands[commandIndex]} --goto "${filePath}:${line}"`
91
- : `${cursorCommands[commandIndex]} "${filePath}"`;
92
-
93
- exec(command, (error, stdout, stderr) => {
94
- if (error && error.code === 127) {
95
- // Command not found, try next one
96
- tryCommand(commandIndex + 1);
97
- } else if (error) {
98
- console.error('Error opening Cursor:', error);
99
- return res.status(500).json({ error: error.message });
100
- } else {
101
- // File opened successfully, now bring Cursor to front
102
- const isMac = process.platform === 'darwin';
103
- if (isMac) {
104
- // Use AppleScript to bring Cursor to the front
105
- exec('osascript -e "tell application \\"Cursor\\" to activate"', (activateError) => {
106
- if (activateError) {
107
- console.log('Could not activate Cursor, but file opened successfully');
108
- }
109
- });
110
-
111
- // Additional command to ensure it's really in front
112
- setTimeout(() => {
113
- exec('osascript -e "tell application \\"System Events\\" to set frontmost of process \\"Cursor\\" to true"', () => {
114
- // Don't worry if this fails
115
- });
116
- }, 500);
117
- }
118
-
119
- res.json({ success: true, message: 'Cursor opened and focused successfully' });
120
- }
121
- });
122
- };
123
-
124
- tryCommand();
125
- } catch (e) {
126
- res.status(500).json({ error: e.message });
127
- }
128
- });
129
-
130
- // POST /shutdown - Gracefully shutdown the server
131
- app.post('/shutdown', (req, res) => {
132
- console.log('🛑 Shutdown endpoint called - terminating server...');
133
-
134
- res.json({
135
- success: true,
136
- message: 'Server shutting down...'
137
- });
138
-
139
- // Close the server gracefully
140
- setTimeout(() => {
141
- process.exit(0);
142
- }, 100); // Small delay to ensure response is sent
143
- });
144
- }
145
-
146
- module.exports = { setupInfraRoutes };
147
-
@@ -1,96 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { getIgnorePatterns, shouldIgnore } = require('./ignore-patterns');
4
- const { extractPythonFunctions, extractJavaScriptFunctions, extractVueFunctions } = require('./parsers');
5
-
6
- // Recursively list all files in a directory, respecting ignore patterns
7
- function listAllFiles(dir, base = '', ignorePatterns = null) {
8
- if (ignorePatterns === null) {
9
- ignorePatterns = getIgnorePatterns();
10
- }
11
-
12
- let results = [];
13
- const list = fs.readdirSync(dir);
14
-
15
- for (const file of list) {
16
- const fullPath = path.join(dir, file);
17
- const relativePath = path.join(base, file);
18
-
19
- // Check if this path should be ignored
20
- if (shouldIgnore(relativePath, ignorePatterns)) {
21
- continue;
22
- }
23
-
24
- const stat = fs.statSync(fullPath);
25
- if (stat && stat.isDirectory()) {
26
- // Skip node_modules directories explicitly
27
- if (file === 'node_modules' || relativePath.includes('node_modules/')) {
28
- continue;
29
- }
30
- // Add the directory itself to results
31
- results.push(relativePath + '/');
32
- // Recursively list files inside the directory
33
- results = results.concat(listAllFiles(fullPath, relativePath, ignorePatterns));
34
- } else {
35
- // Skip files in node_modules explicitly
36
- if (relativePath.includes('node_modules/') || fullPath.includes('node_modules')) {
37
- continue;
38
- }
39
-
40
- results.push(relativePath);
41
-
42
- // Helper function to add functions, classes, and methods as virtual files
43
- const addVirtualFiles = (parseResult, filePath) => {
44
- // Add functions
45
- for (const func of parseResult.functions) {
46
- const functionFileName = func.name + '.function';
47
- const functionFilePath = path.join(filePath, functionFileName).replace(/\\/g, '/');
48
- results.push(functionFilePath);
49
- }
50
-
51
- // Add classes and their methods
52
- for (const cls of parseResult.classes) {
53
- // Add class itself (optional, but useful)
54
- const className = cls.name + '.class';
55
- const classFilePath = path.join(filePath, className).replace(/\\/g, '/');
56
- results.push(classFilePath);
57
-
58
- // Add methods nested under the class: ClassName.methodName
59
- if (cls.methods && cls.methods.length > 0) {
60
- for (const method of cls.methods) {
61
- const methodFileName = method.name + '.method';
62
- const methodFilePath = path.join(classFilePath, methodFileName).replace(/\\/g, '/');
63
- results.push(methodFilePath);
64
- }
65
- }
66
- }
67
- };
68
-
69
- // Handle Python files
70
- if (file.endsWith('.py')) {
71
- const parseResult = extractPythonFunctions(fullPath);
72
- addVirtualFiles(parseResult, relativePath);
73
- }
74
-
75
- // Handle JavaScript/TypeScript files
76
- // Skip .d.ts files (TypeScript declaration files) and .min.js files (minified)
77
- if ((file.endsWith('.js') || file.endsWith('.jsx') || file.endsWith('.ts') || file.endsWith('.tsx')) &&
78
- !file.endsWith('.d.ts') && !file.endsWith('.min.js')) {
79
- const parseResult = extractJavaScriptFunctions(fullPath);
80
- addVirtualFiles(parseResult, relativePath);
81
- }
82
-
83
- // Handle Vue files
84
- if (file.endsWith('.vue')) {
85
- const parseResult = extractVueFunctions(fullPath);
86
- addVirtualFiles(parseResult, relativePath);
87
- }
88
- }
89
- }
90
- return results;
91
- }
92
-
93
- module.exports = {
94
- listAllFiles
95
- };
96
-
@@ -1,53 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- // Read and parse ignore patterns from .ignore file
5
- function getIgnorePatterns() {
6
- try {
7
- const ignorePath = path.join(__dirname, '..', '.ignore');
8
- if (fs.existsSync(ignorePath)) {
9
- const content = fs.readFileSync(ignorePath, 'utf8');
10
- return content
11
- .split('\n')
12
- .map(line => line.trim())
13
- .filter(line => line && !line.startsWith('#'))
14
- .map(pattern => {
15
- // Convert simple glob patterns to regex
16
- if (pattern.endsWith('/')) {
17
- // Directory pattern
18
- pattern = pattern.slice(0, -1);
19
- }
20
-
21
- // Simple approach: escape dots and convert globs
22
- pattern = pattern.replace(/\./g, '\\.');
23
- pattern = pattern.replace(/\*\*/g, '|||DOUBLESTAR|||');
24
- pattern = pattern.replace(/\*/g, '[^/]*');
25
- pattern = pattern.replace(/\|\|\|DOUBLESTAR\|\|\|/g, '.*');
26
-
27
- return new RegExp('^' + pattern + '(/.*)?$');
28
- });
29
- }
30
- } catch (e) {
31
- console.warn('Could not read .ignore file:', e.message);
32
- }
33
-
34
- // Default ignore patterns if no .ignore file exists
35
- return [
36
- /^node_modules(\/.*)?$/,
37
- /^\.git(\/.*)?$/,
38
- /^\.DS_Store$/,
39
- /^\.env$/,
40
- /^bonzai(\/.*)?$/
41
- ];
42
- }
43
-
44
- // Check if a path should be ignored
45
- function shouldIgnore(relativePath, ignorePatterns) {
46
- return ignorePatterns.some(pattern => pattern.test(relativePath));
47
- }
48
-
49
- module.exports = {
50
- getIgnorePatterns,
51
- shouldIgnore
52
- };
53
-