recoder-code 2.3.0 → 2.3.5

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.
@@ -17,26 +17,32 @@ class EnhancedPluginManager {
17
17
  }
18
18
  }
19
19
 
20
- async loadPlugins() {
20
+ async loadPlugins(silent = false) {
21
21
  try {
22
22
  const pluginFiles = fs.readdirSync(this.pluginDir)
23
23
  .filter(file => path.extname(file) === '.js' && !file.startsWith('enhanced-plugin-manager'));
24
24
 
25
25
  for (const file of pluginFiles) {
26
26
  try {
27
- await this.loadPlugin(file);
27
+ await this.loadPlugin(file, silent);
28
28
  } catch (error) {
29
- console.log(chalk.yellow(`⚠️ Failed to load plugin ${file}: ${error.message}`));
29
+ if (!silent) {
30
+ console.log(chalk.yellow(`⚠️ Failed to load plugin ${file}: ${error.message}`));
31
+ }
30
32
  }
31
33
  }
32
34
 
33
- console.log(chalk.blue(`🔌 Loaded ${this.plugins.size} plugins`));
35
+ if (!silent) {
36
+ console.log(chalk.blue(`🔌 Loaded ${this.plugins.size} plugins`));
37
+ }
34
38
  } catch (error) {
35
- console.log(chalk.yellow(`⚠️ Could not load plugins: ${error.message}`));
39
+ if (!silent) {
40
+ console.log(chalk.yellow(`⚠️ Could not load plugins: ${error.message}`));
41
+ }
36
42
  }
37
43
  }
38
44
 
39
- async loadPlugin(filename) {
45
+ async loadPlugin(filename, silent = false) {
40
46
  const pluginPath = path.join(this.pluginDir, filename);
41
47
 
42
48
  // Clear require cache to allow plugin reloading
@@ -112,13 +118,19 @@ class EnhancedPluginManager {
112
118
  if (plugin.enabled) {
113
119
  try {
114
120
  await plugin.init();
115
- console.log(chalk.green(`✅ Loaded plugin: ${plugin.name} v${plugin.version}`));
121
+ if (!silent) {
122
+ console.log(chalk.green(`✅ Loaded plugin: ${plugin.name} v${plugin.version}`));
123
+ }
116
124
  } catch (error) {
117
- console.log(chalk.red(`❌ Failed to initialize plugin ${plugin.name}: ${error.message}`));
125
+ if (!silent) {
126
+ console.log(chalk.red(`❌ Failed to initialize plugin ${plugin.name}: ${error.message}`));
127
+ }
118
128
  plugin.enabled = false;
119
129
  }
120
130
  } else {
121
- console.log(chalk.gray(`⏸️ Plugin disabled: ${plugin.name}`));
131
+ if (!silent) {
132
+ console.log(chalk.gray(`⏸️ Plugin disabled: ${plugin.name}`));
133
+ }
122
134
  }
123
135
  }
124
136
 
@@ -13,7 +13,7 @@ module.exports = {
13
13
  enabled: true,
14
14
 
15
15
  async init() {
16
- console.log(chalk.green('✅ IDE integration plugin initialized'));
16
+ // console.log(chalk.green('✅ IDE integration plugin initialized'));
17
17
  },
18
18
 
19
19
  async cleanup() {
@@ -0,0 +1,235 @@
1
+ // IDE Integration Plugin for Recoder
2
+ // Handle chalk properly
3
+ const chalkModule = require('chalk');
4
+ const chalk = chalkModule.default || chalkModule;
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ module.exports = {
9
+ name: 'ide-integration',
10
+ version: '1.0.0',
11
+ description: 'Enhanced IDE integration features and file operations',
12
+ author: 'Recoder Team',
13
+ enabled: true,
14
+
15
+ async init() {
16
+ console.log(chalk.green('✅ IDE integration plugin initialized'));
17
+ },
18
+
19
+ async cleanup() {
20
+ console.log('IDE integration plugin cleaned up');
21
+ },
22
+
23
+ commands: {
24
+ 'edit': async (args, context) => {
25
+ const chat = context.chat;
26
+ const filePath = args.join(' ');
27
+
28
+ if (!filePath) {
29
+ return '❌ Usage: /edit <file-path>';
30
+ }
31
+
32
+ if (!chat.ideIntegration) {
33
+ return '❌ IDE integration not available';
34
+ }
35
+
36
+ // Quick edit command - opens file in IDE
37
+ const success = await chat.ideIntegration.openFileInIDE(filePath);
38
+ if (success) {
39
+ return `✅ Opened ${filePath} in your IDE`;
40
+ } else {
41
+ return `⚠️ Opened ${filePath} in default editor`;
42
+ }
43
+ },
44
+
45
+ 'find': async (args, context) => {
46
+ if (!args.length) {
47
+ return '❌ Usage: /find <search-term>';
48
+ }
49
+
50
+ const searchTerm = args.join(' ');
51
+ const { exec } = require('child_process');
52
+
53
+ return new Promise((resolve) => {
54
+ // Use ripgrep if available, otherwise grep
55
+ exec(`rg --line-number "${searchTerm}" || grep -rn "${searchTerm}" .`, (error, stdout) => {
56
+ if (error) {
57
+ resolve(`❌ Search failed: ${error.message}`);
58
+ return;
59
+ }
60
+
61
+ if (!stdout.trim()) {
62
+ resolve(`🔍 No results found for: ${searchTerm}`);
63
+ return;
64
+ }
65
+
66
+ const lines = stdout.trim().split('\n').slice(0, 10); // Limit to 10 results
67
+ let result = `🔍 Search results for "${searchTerm}":\n\n`;
68
+
69
+ lines.forEach(line => {
70
+ const parts = line.split(':');
71
+ if (parts.length >= 3) {
72
+ const file = parts[0];
73
+ const lineNum = parts[1];
74
+ const content = parts.slice(2).join(':').trim();
75
+ result += `📄 ${file}:${lineNum}\n ${content}\n\n`;
76
+ }
77
+ });
78
+
79
+ result += `💡 Tip: Use /goto <file>:<line> to jump to a result`;
80
+ resolve(result);
81
+ });
82
+ });
83
+ },
84
+
85
+ 'tree': async (args, context) => {
86
+ const depth = args[0] ? parseInt(args[0]) : 2;
87
+ const { exec } = require('child_process');
88
+
89
+ return new Promise((resolve) => {
90
+ exec(`tree -L ${depth} -I 'node_modules|.git|dist|build' || find . -type d -name 'node_modules' -prune -o -type d -name '.git' -prune -o -type f -print | head -20`, (error, stdout) => {
91
+ if (error) {
92
+ resolve('❌ Could not generate project tree');
93
+ return;
94
+ }
95
+
96
+ resolve(`📁 Project Structure:\n\`\`\`\n${stdout}\n\`\`\``);
97
+ });
98
+ });
99
+ },
100
+
101
+ 'workspace-info': async (args, context) => {
102
+ const chat = context.chat;
103
+ if (!chat.ideIntegration) {
104
+ return '❌ IDE integration not available';
105
+ }
106
+
107
+ const context_info = chat.ideIntegration.getCurrentContext();
108
+ let result = '📍 Workspace Information:\n\n';
109
+
110
+ result += `🔧 IDE: ${context_info.ide}\n`;
111
+ result += `📁 Workspace Root: ${context_info.workspaceRoot}\n`;
112
+ result += `📂 Current Directory: ${process.cwd()}\n`;
113
+ result += `🎯 Go-to Support: ${context_info.supportsGoto ? 'Yes' : 'No'}\n`;
114
+ result += `🐛 Debug Support: ${context_info.supportsDebug ? 'Yes' : 'No'}\n\n`;
115
+
116
+ // Check for common project files
117
+ const projectFiles = [
118
+ 'package.json',
119
+ 'Cargo.toml',
120
+ 'pyproject.toml',
121
+ 'requirements.txt',
122
+ 'pom.xml',
123
+ 'build.gradle',
124
+ 'Makefile',
125
+ 'docker-compose.yml',
126
+ 'README.md'
127
+ ];
128
+
129
+ const foundFiles = projectFiles.filter(file =>
130
+ fs.existsSync(path.join(context_info.workspaceRoot, file))
131
+ );
132
+
133
+ if (foundFiles.length > 0) {
134
+ result += `📋 Project Files Found:\n`;
135
+ foundFiles.forEach(file => {
136
+ result += `• ${file}\n`;
137
+ });
138
+ }
139
+
140
+ return result;
141
+ },
142
+
143
+ 'quick-setup': async (args, context) => {
144
+ const chat = context.chat;
145
+ if (!chat.ideIntegration) {
146
+ return '❌ IDE integration not available';
147
+ }
148
+
149
+ const context_info = chat.ideIntegration.getCurrentContext();
150
+
151
+ let result = '🚀 Quick IDE Setup Guide:\n\n';
152
+
153
+ if (context_info.ide === 'vscode') {
154
+ result += '📝 VS Code Setup:\n';
155
+ result += '1. Run: /ide setup (creates .vscode config)\n';
156
+ result += '2. Install recommended extensions:\n';
157
+ result += ' • Better Comments\n';
158
+ result += ' • GitLens\n';
159
+ result += ' • Prettier\n';
160
+ result += ' • ESLint (for JS/TS)\n';
161
+ result += '3. Use Ctrl+Shift+P -> "Tasks: Run Task" -> "Open Recoder Chat"\n\n';
162
+ } else if (context_info.ide === 'jetbrains') {
163
+ result += '🧠 JetBrains IDE Setup:\n';
164
+ result += '1. Configure terminal to use your shell\n';
165
+ result += '2. Add external tool for Recoder\n';
166
+ result += '3. Set up custom live templates for AI prompts\n\n';
167
+ } else {
168
+ result += `🖥️ Terminal Setup (${context_info.ide}):\n`;
169
+ result += '1. Ensure recoder is in your PATH\n';
170
+ result += '2. Create shell aliases for quick access\n';
171
+ result += '3. Set EDITOR environment variable\n\n';
172
+ }
173
+
174
+ result += '💡 Pro Tips:\n';
175
+ result += '• Use /open <file> to quickly open files\n';
176
+ result += '• Use /goto <file>:<line> for precise navigation\n';
177
+ result += '• Use /find <term> to search across project\n';
178
+ result += '• Use /tree to see project structure\n';
179
+
180
+ return result;
181
+ }
182
+ },
183
+
184
+ // Process messages for IDE-related suggestions
185
+ onMessage: async (message, context) => {
186
+ const lowerMessage = message.toLowerCase();
187
+
188
+ // Suggest IDE commands for file operations
189
+ if (lowerMessage.includes('open file') || lowerMessage.includes('edit file')) {
190
+ return '💡 Tip: Use /open <filename> to open files in your IDE';
191
+ }
192
+
193
+ if (lowerMessage.includes('go to line') || lowerMessage.includes('jump to')) {
194
+ return '💡 Tip: Use /goto <file>:<line> to navigate to specific lines';
195
+ }
196
+
197
+ if (lowerMessage.includes('search for') || lowerMessage.includes('find in project')) {
198
+ return '💡 Tip: Use /find <search-term> to search across your project';
199
+ }
200
+
201
+ if (lowerMessage.includes('project structure') || lowerMessage.includes('file tree')) {
202
+ return '💡 Tip: Use /tree to see your project structure';
203
+ }
204
+
205
+ if (lowerMessage.includes('ide setup') || lowerMessage.includes('configure editor')) {
206
+ return '💡 Tip: Use /quick-setup for IDE configuration guidance';
207
+ }
208
+
209
+ return null;
210
+ },
211
+
212
+ // Hook into response processing to add file references
213
+ onResponse: async (response, context) => {
214
+ const chat = context.chat;
215
+
216
+ // Look for file references in AI responses and make them clickable
217
+ if (chat.ideIntegration && response.includes('.js') || response.includes('.ts') || response.includes('.py')) {
218
+ // This could be enhanced to detect actual file references
219
+ // and convert them to clickable links for supported IDEs
220
+ console.log(chalk.gray('💡 Tip: Use /open <filename> to edit mentioned files'));
221
+ }
222
+ },
223
+
224
+ hooks: {
225
+ beforeResponse: async (data) => {
226
+ // Could add IDE context to prompts here
227
+ return data;
228
+ },
229
+
230
+ afterResponse: async (data) => {
231
+ // Could add IDE-specific formatting here
232
+ return data;
233
+ }
234
+ }
235
+ };
@@ -19,7 +19,7 @@ module.exports = {
19
19
  async init() {
20
20
  // Load saved data
21
21
  this.loadData();
22
- console.log('Productivity plugin initialized');
22
+ // console.log('Productivity plugin initialized');
23
23
  },
24
24
 
25
25
  async cleanup() {
@@ -0,0 +1,215 @@
1
+ // Productivity Plugin for Recoder
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ module.exports = {
6
+ name: 'productivity',
7
+ version: '1.0.0',
8
+ description: 'Productivity tools like notes, todos, and reminders',
9
+ author: 'Recoder Team',
10
+ enabled: true,
11
+
12
+ // Internal state
13
+ state: {
14
+ todos: [],
15
+ notes: [],
16
+ timers: new Map()
17
+ },
18
+
19
+ async init() {
20
+ // Load saved data
21
+ this.loadData();
22
+ console.log('Productivity plugin initialized');
23
+ },
24
+
25
+ async cleanup() {
26
+ // Save data
27
+ this.saveData();
28
+ console.log('Productivity plugin cleaned up');
29
+ },
30
+
31
+ commands: {
32
+ todo: async (args, context) => {
33
+ if (!args.length) {
34
+ // List todos
35
+ if (this.state.todos.length === 0) {
36
+ return '📝 No todos found. Add one with: /todo add <task>';
37
+ }
38
+
39
+ let result = '📝 Your Todos:\n';
40
+ this.state.todos.forEach((todo, index) => {
41
+ const status = todo.completed ? '✅' : '⏳';
42
+ result += `${index + 1}. ${status} ${todo.task}\n`;
43
+ });
44
+ return result;
45
+ }
46
+
47
+ const action = args[0].toLowerCase();
48
+ const taskText = args.slice(1).join(' ');
49
+
50
+ switch (action) {
51
+ case 'add':
52
+ if (!taskText) return 'Usage: /todo add <task>';
53
+ this.state.todos.push({
54
+ task: taskText,
55
+ completed: false,
56
+ created: new Date().toISOString()
57
+ });
58
+ this.saveData();
59
+ return `✅ Added todo: ${taskText}`;
60
+
61
+ case 'done':
62
+ const doneIndex = parseInt(args[1]) - 1;
63
+ if (isNaN(doneIndex) || !this.state.todos[doneIndex]) {
64
+ return 'Usage: /todo done <number>';
65
+ }
66
+ this.state.todos[doneIndex].completed = true;
67
+ this.saveData();
68
+ return `✅ Completed: ${this.state.todos[doneIndex].task}`;
69
+
70
+ case 'clear':
71
+ this.state.todos = this.state.todos.filter(todo => !todo.completed);
72
+ this.saveData();
73
+ return '🗑️ Cleared completed todos';
74
+
75
+ default:
76
+ return 'Usage: /todo [add|done|clear] or /todo to list';
77
+ }
78
+ },
79
+
80
+ note: async (args, context) => {
81
+ if (!args.length) {
82
+ // List notes
83
+ if (this.state.notes.length === 0) {
84
+ return '📝 No notes found. Add one with: /note add <content>';
85
+ }
86
+
87
+ let result = '📝 Your Notes:\n';
88
+ this.state.notes.forEach((note, index) => {
89
+ const preview = note.content.substring(0, 50);
90
+ result += `${index + 1}. ${preview}${note.content.length > 50 ? '...' : ''}\n`;
91
+ });
92
+ return result;
93
+ }
94
+
95
+ const action = args[0].toLowerCase();
96
+ const content = args.slice(1).join(' ');
97
+
98
+ switch (action) {
99
+ case 'add':
100
+ if (!content) return 'Usage: /note add <content>';
101
+ this.state.notes.push({
102
+ content: content,
103
+ created: new Date().toISOString()
104
+ });
105
+ this.saveData();
106
+ return `📝 Added note: ${content.substring(0, 30)}...`;
107
+
108
+ case 'show':
109
+ const showIndex = parseInt(args[1]) - 1;
110
+ if (isNaN(showIndex) || !this.state.notes[showIndex]) {
111
+ return 'Usage: /note show <number>';
112
+ }
113
+ return `📝 Note ${showIndex + 1}:\n${this.state.notes[showIndex].content}`;
114
+
115
+ case 'delete':
116
+ const deleteIndex = parseInt(args[1]) - 1;
117
+ if (isNaN(deleteIndex) || !this.state.notes[deleteIndex]) {
118
+ return 'Usage: /note delete <number>';
119
+ }
120
+ const deleted = this.state.notes.splice(deleteIndex, 1)[0];
121
+ this.saveData();
122
+ return `🗑️ Deleted note: ${deleted.content.substring(0, 30)}...`;
123
+
124
+ default:
125
+ return 'Usage: /note [add|show|delete] or /note to list';
126
+ }
127
+ },
128
+
129
+ timer: async (args, context) => {
130
+ if (!args.length) {
131
+ // Show active timers
132
+ if (this.state.timers.size === 0) {
133
+ return '⏰ No active timers. Start one with: /timer <minutes> [name]';
134
+ }
135
+
136
+ let result = '⏰ Active Timers:\n';
137
+ for (const [name, timer] of this.state.timers) {
138
+ const remaining = Math.max(0, timer.duration - (Date.now() - timer.start));
139
+ const remainingMinutes = Math.ceil(remaining / 60000);
140
+ result += `${name}: ${remainingMinutes} minutes remaining\n`;
141
+ }
142
+ return result;
143
+ }
144
+
145
+ const minutes = parseInt(args[0]);
146
+ if (isNaN(minutes) || minutes <= 0) {
147
+ return 'Usage: /timer <minutes> [name]';
148
+ }
149
+
150
+ const name = args.slice(1).join(' ') || `Timer-${Date.now()}`;
151
+ const duration = minutes * 60 * 1000;
152
+
153
+ this.state.timers.set(name, {
154
+ start: Date.now(),
155
+ duration: duration
156
+ });
157
+
158
+ // Set timeout to notify when done
159
+ setTimeout(() => {
160
+ this.state.timers.delete(name);
161
+ // In a real implementation, you'd want to notify the user
162
+ console.log(`⏰ Timer "${name}" completed!`);
163
+ }, duration);
164
+
165
+ return `⏰ Timer "${name}" started for ${minutes} minutes`;
166
+ }
167
+ },
168
+
169
+ // Process messages for productivity hints
170
+ onMessage: async (message, context) => {
171
+ const lowerMessage = message.toLowerCase();
172
+
173
+ // Suggest productivity commands
174
+ if (lowerMessage.includes('remind') || lowerMessage.includes('remember')) {
175
+ return '💡 Tip: Use /note add to save important information';
176
+ }
177
+
178
+ if (lowerMessage.includes('task') || lowerMessage.includes('todo')) {
179
+ return '💡 Tip: Use /todo add to track tasks';
180
+ }
181
+
182
+ if (lowerMessage.includes('focus') || lowerMessage.includes('pomodoro')) {
183
+ return '💡 Tip: Use /timer 25 pomodoro for focused work sessions';
184
+ }
185
+
186
+ return null;
187
+ },
188
+
189
+ loadData() {
190
+ try {
191
+ const dataPath = path.join(process.cwd(), 'plugins', 'productivity-data.json');
192
+ if (fs.existsSync(dataPath)) {
193
+ const data = JSON.parse(fs.readFileSync(dataPath, 'utf8'));
194
+ this.state.todos = data.todos || [];
195
+ this.state.notes = data.notes || [];
196
+ }
197
+ } catch (error) {
198
+ console.log('Could not load productivity data:', error.message);
199
+ }
200
+ },
201
+
202
+ saveData() {
203
+ try {
204
+ const dataPath = path.join(process.cwd(), 'plugins', 'productivity-data.json');
205
+ const data = {
206
+ todos: this.state.todos,
207
+ notes: this.state.notes,
208
+ lastSaved: new Date().toISOString()
209
+ };
210
+ fs.writeFileSync(dataPath, JSON.stringify(data, null, 2));
211
+ } catch (error) {
212
+ console.log('Could not save productivity data:', error.message);
213
+ }
214
+ }
215
+ };
@@ -2,6 +2,6 @@ module.exports = {
2
2
  name: 'Sample Plugin',
3
3
  description: 'This is a sample plugin for demonstration purposes.',
4
4
  init: () => {
5
- console.log('Sample Plugin initialized');
5
+ // console.log('Sample Plugin initialized');
6
6
  }
7
7
  };
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ name: 'Sample Plugin',
3
+ description: 'This is a sample plugin for demonstration purposes.',
4
+ init: () => {
5
+ console.log('Sample Plugin initialized');
6
+ }
7
+ };
@@ -13,7 +13,7 @@ module.exports = {
13
13
  enabled: true,
14
14
 
15
15
  async init() {
16
- console.log(chalk.green('✅ Team collaboration plugin initialized'));
16
+ // console.log(chalk.green('✅ Team collaboration plugin initialized'));
17
17
  },
18
18
 
19
19
  async cleanup() {