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.
- package/cli/interactive.js +194 -125
- package/cli/run.js +61 -19
- package/cli/user-onboarding.js +19 -2
- package/config.js +4 -3
- package/package.json +2 -2
- package/plugins/enhanced-plugin-manager.js +21 -9
- package/plugins/ide-integration-plugin.js +1 -1
- package/plugins/ide-integration-plugin.js.bak +235 -0
- package/plugins/productivity-plugin.js +1 -1
- package/plugins/productivity-plugin.js.bak +215 -0
- package/plugins/sample-plugin.js +1 -1
- package/plugins/sample-plugin.js.bak +7 -0
- package/plugins/team-collaboration-plugin.js +1 -1
- package/plugins/team-collaboration-plugin.js.bak +208 -0
- package/plugins/voice-commands-plugin.js +1 -1
- package/plugins/voice-commands-plugin.js.bak +136 -0
- package/plugins/weather-plugin.js +1 -1
- package/plugins/weather-plugin.js.bak +83 -0
|
@@ -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
|
-
|
|
29
|
+
if (!silent) {
|
|
30
|
+
console.log(chalk.yellow(`⚠️ Failed to load plugin ${file}: ${error.message}`));
|
|
31
|
+
}
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
if (!silent) {
|
|
36
|
+
console.log(chalk.blue(`🔌 Loaded ${this.plugins.size} plugins`));
|
|
37
|
+
}
|
|
34
38
|
} catch (error) {
|
|
35
|
-
|
|
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
|
-
|
|
121
|
+
if (!silent) {
|
|
122
|
+
console.log(chalk.green(`✅ Loaded plugin: ${plugin.name} v${plugin.version}`));
|
|
123
|
+
}
|
|
116
124
|
} catch (error) {
|
|
117
|
-
|
|
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
|
-
|
|
131
|
+
if (!silent) {
|
|
132
|
+
console.log(chalk.gray(`⏸️ Plugin disabled: ${plugin.name}`));
|
|
133
|
+
}
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
136
|
|
|
@@ -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
|
+
};
|
|
@@ -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
|
+
};
|
package/plugins/sample-plugin.js
CHANGED