recoder-code 2.2.0 → 2.2.2
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/README.md +66 -10
- package/cli/collaboration-manager.js +3 -1
- package/cli/dev-tools-integration.js +3 -1
- package/cli/enhanced-setup.js +3 -1
- package/cli/file-watcher-manager.js +216 -0
- package/cli/interactive.js +2258 -165
- package/cli/logger.js +181 -0
- package/cli/mcp-client.js +32 -9
- package/cli/ml-training-manager.js +3 -1
- package/cli/model-manager.js +3 -1
- package/cli/natural-language-processor.js +3 -1
- package/cli/project-manager.js +3 -1
- package/cli/rules-engine.js +9 -4
- package/cli/run.js +90 -13
- package/cli/setup-wizard.js +3 -1
- package/cli/slash-commands.js +132 -5
- package/cli/task-manager.js +13 -5
- package/cli/team-collaboration.js +3 -1
- package/cli/todo-manager.js +391 -0
- package/cli/unified-error-handler.js +260 -0
- package/cli/user-experience.js +3 -1
- package/cli/user-onboarding.js +251 -0
- package/cli/vision-analyzer.js +3 -1
- package/index.js +88 -13
- package/package.json +7 -1
- package/plugins/enhanced-plugin-manager.js +31 -11
- package/scripts/performance-benchmark.js +3 -1
- package/scripts/security-audit.js +3 -1
- package/setup.js +3 -1
package/cli/slash-commands.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
|
|
5
|
+
// Handle chalk properly
|
|
6
|
+
const chalkModule = require('chalk');
|
|
7
|
+
const chalk = chalkModule.default || chalkModule;
|
|
6
8
|
const inquirer = require('inquirer');
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -10,10 +12,11 @@ const inquirer = require('inquirer');
|
|
|
10
12
|
* Flexible AI agent framework for specialized tasks
|
|
11
13
|
*/
|
|
12
14
|
class SlashCommandsEngine {
|
|
13
|
-
constructor(contextEngine, projectManager, visionAnalyzer = null) {
|
|
15
|
+
constructor(contextEngine, projectManager, visionAnalyzer = null, todoManager = null) {
|
|
14
16
|
this.contextEngine = contextEngine;
|
|
15
17
|
this.projectManager = projectManager;
|
|
16
18
|
this.visionAnalyzer = visionAnalyzer;
|
|
19
|
+
this.todoManager = todoManager;
|
|
17
20
|
this.commands = new Map();
|
|
18
21
|
this.aliases = new Map();
|
|
19
22
|
this.commandHistory = [];
|
|
@@ -186,6 +189,21 @@ class SlashCommandsEngine {
|
|
|
186
189
|
});
|
|
187
190
|
}
|
|
188
191
|
|
|
192
|
+
// Todo Management Commands for Production Use
|
|
193
|
+
this.registerCommand({
|
|
194
|
+
name: 'todo',
|
|
195
|
+
aliases: ['task', 'todos'],
|
|
196
|
+
description: 'Production-ready todo and task management system',
|
|
197
|
+
category: 'productivity',
|
|
198
|
+
agent: this.createTodoAgent(),
|
|
199
|
+
options: {
|
|
200
|
+
action: { type: 'string', default: 'list', choices: ['list', 'add', 'complete', 'start', 'delete', 'stats', 'search'] },
|
|
201
|
+
priority: { type: 'string', default: 'medium', choices: ['low', 'medium', 'high'] },
|
|
202
|
+
category: { type: 'string', default: 'general' },
|
|
203
|
+
show_completed: { type: 'boolean', default: true }
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
189
207
|
console.log(chalk.green(`✅ Loaded ${this.commands.size} built-in commands`));
|
|
190
208
|
}
|
|
191
209
|
|
|
@@ -234,7 +252,7 @@ class SlashCommandsEngine {
|
|
|
234
252
|
const mergedOptions = this.mergeOptions(commandDef.options, args, options);
|
|
235
253
|
|
|
236
254
|
// Generate context for the command
|
|
237
|
-
const context = await this.generateCommandContext(commandDef, mergedOptions);
|
|
255
|
+
const context = await this.generateCommandContext(commandDef, mergedOptions, args);
|
|
238
256
|
|
|
239
257
|
// Execute the agent
|
|
240
258
|
const result = await commandDef.agent.execute(context, mergedOptions);
|
|
@@ -310,14 +328,16 @@ class SlashCommandsEngine {
|
|
|
310
328
|
/**
|
|
311
329
|
* Generate context for command execution
|
|
312
330
|
*/
|
|
313
|
-
async generateCommandContext(commandDef, options) {
|
|
331
|
+
async generateCommandContext(commandDef, options, args = []) {
|
|
314
332
|
const context = {
|
|
315
333
|
command: commandDef.name,
|
|
316
334
|
category: commandDef.category,
|
|
317
335
|
options: options,
|
|
336
|
+
args: args,
|
|
318
337
|
codebase: null,
|
|
319
338
|
relevantFiles: [],
|
|
320
|
-
projectInfo: null
|
|
339
|
+
projectInfo: null,
|
|
340
|
+
todoManager: this.todoManager
|
|
321
341
|
};
|
|
322
342
|
|
|
323
343
|
// Get codebase context if context engine is available
|
|
@@ -1185,6 +1205,106 @@ class SlashCommandsEngine {
|
|
|
1185
1205
|
}
|
|
1186
1206
|
}
|
|
1187
1207
|
|
|
1208
|
+
/**
|
|
1209
|
+
* Create Todo Management Agent
|
|
1210
|
+
*/
|
|
1211
|
+
createTodoAgent() {
|
|
1212
|
+
return {
|
|
1213
|
+
name: 'TodoAgent',
|
|
1214
|
+
async execute(context, options) {
|
|
1215
|
+
// Get todo manager from interactive instance
|
|
1216
|
+
const todoManager = context.todoManager || new (require('./todo-manager.js'))();
|
|
1217
|
+
|
|
1218
|
+
switch (options.action) {
|
|
1219
|
+
case 'list':
|
|
1220
|
+
console.log('');
|
|
1221
|
+
todoManager.displayTodos(options.show_completed);
|
|
1222
|
+
break;
|
|
1223
|
+
|
|
1224
|
+
case 'add':
|
|
1225
|
+
if (!context.args || context.args.length === 0) {
|
|
1226
|
+
console.log(chalk.yellow('⚠️ Usage: /todo add <task description>'));
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
const content = context.args.join(' ');
|
|
1230
|
+
const id = todoManager.addTodo(content, options.priority, options.category);
|
|
1231
|
+
console.log(chalk.green(`✅ Added todo: ${content}`));
|
|
1232
|
+
console.log(chalk.gray(` ID: ${id}, Priority: ${options.priority}, Category: ${options.category}`));
|
|
1233
|
+
break;
|
|
1234
|
+
|
|
1235
|
+
case 'complete':
|
|
1236
|
+
if (!context.args || context.args.length === 0) {
|
|
1237
|
+
console.log(chalk.yellow('⚠️ Usage: /todo complete <id>'));
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
if (todoManager.completeTodo(context.args[0])) {
|
|
1241
|
+
console.log(chalk.green('✅ Todo marked as completed'));
|
|
1242
|
+
} else {
|
|
1243
|
+
console.log(chalk.red('❌ Todo not found'));
|
|
1244
|
+
}
|
|
1245
|
+
break;
|
|
1246
|
+
|
|
1247
|
+
case 'start':
|
|
1248
|
+
if (!context.args || context.args.length === 0) {
|
|
1249
|
+
console.log(chalk.yellow('⚠️ Usage: /todo start <id>'));
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
if (todoManager.startTodo(context.args[0])) {
|
|
1253
|
+
console.log(chalk.yellow('◐ Todo marked as in progress'));
|
|
1254
|
+
} else {
|
|
1255
|
+
console.log(chalk.red('❌ Todo not found'));
|
|
1256
|
+
}
|
|
1257
|
+
break;
|
|
1258
|
+
|
|
1259
|
+
case 'delete':
|
|
1260
|
+
if (!context.args || context.args.length === 0) {
|
|
1261
|
+
console.log(chalk.yellow('⚠️ Usage: /todo delete <id>'));
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
if (todoManager.deleteTodo(context.args[0])) {
|
|
1265
|
+
console.log(chalk.green('✅ Todo deleted'));
|
|
1266
|
+
} else {
|
|
1267
|
+
console.log(chalk.red('❌ Todo not found'));
|
|
1268
|
+
}
|
|
1269
|
+
break;
|
|
1270
|
+
|
|
1271
|
+
case 'stats':
|
|
1272
|
+
console.log('');
|
|
1273
|
+
todoManager.displayStats();
|
|
1274
|
+
break;
|
|
1275
|
+
|
|
1276
|
+
case 'search':
|
|
1277
|
+
if (!context.args || context.args.length === 0) {
|
|
1278
|
+
console.log(chalk.yellow('⚠️ Usage: /todo search <query>'));
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
const query = context.args.join(' ');
|
|
1282
|
+
todoManager.searchTodos(query);
|
|
1283
|
+
break;
|
|
1284
|
+
|
|
1285
|
+
default:
|
|
1286
|
+
console.log(chalk.cyan.bold('Todo Management System'));
|
|
1287
|
+
console.log('');
|
|
1288
|
+
console.log(chalk.white('Available actions:'));
|
|
1289
|
+
console.log(chalk.gray(' /todo list - Show all todos'));
|
|
1290
|
+
console.log(chalk.gray(' /todo add <task> - Add new todo'));
|
|
1291
|
+
console.log(chalk.gray(' /todo complete <id> - Mark todo as complete'));
|
|
1292
|
+
console.log(chalk.gray(' /todo start <id> - Mark todo as in progress'));
|
|
1293
|
+
console.log(chalk.gray(' /todo delete <id> - Delete todo'));
|
|
1294
|
+
console.log(chalk.gray(' /todo stats - Show statistics'));
|
|
1295
|
+
console.log(chalk.gray(' /todo search <query> - Search todos'));
|
|
1296
|
+
break;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
return {
|
|
1300
|
+
success: true,
|
|
1301
|
+
action: options.action,
|
|
1302
|
+
timestamp: new Date().toISOString()
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1188
1308
|
/**
|
|
1189
1309
|
* Get command suggestions based on context
|
|
1190
1310
|
*/
|
|
@@ -1203,6 +1323,13 @@ class SlashCommandsEngine {
|
|
|
1203
1323
|
suggestions.push('/review --focus performance');
|
|
1204
1324
|
}
|
|
1205
1325
|
|
|
1326
|
+
// Todo-focused suggestions
|
|
1327
|
+
if (context.query && (context.query.toLowerCase().includes('todo') || context.query.toLowerCase().includes('task'))) {
|
|
1328
|
+
suggestions.push('/todo list');
|
|
1329
|
+
suggestions.push('/todo add <task description>');
|
|
1330
|
+
suggestions.push('/todo stats');
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1206
1333
|
return suggestions;
|
|
1207
1334
|
}
|
|
1208
1335
|
}
|
package/cli/task-manager.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
|
|
5
|
+
// Handle chalk properly
|
|
6
|
+
const chalkModule = require('chalk');
|
|
7
|
+
const chalk = chalkModule.default || chalkModule;
|
|
6
8
|
const { EventEmitter } = require('events');
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -482,7 +484,7 @@ class TaskManager extends EventEmitter {
|
|
|
482
484
|
console.log(chalk.gray('🤖 Starting automation engine...'));
|
|
483
485
|
|
|
484
486
|
// Set up file watchers for automated triggers
|
|
485
|
-
if (this.contextEngine && this.contextEngine.fileWatcher) {
|
|
487
|
+
if (this.contextEngine && this.contextEngine.fileWatcher && typeof this.contextEngine.on === 'function') {
|
|
486
488
|
this.contextEngine.on('fileChanged', (filePath, eventType) => {
|
|
487
489
|
this.handleFileChangeAutomation(filePath, eventType);
|
|
488
490
|
});
|
|
@@ -1080,13 +1082,19 @@ class TaskManager extends EventEmitter {
|
|
|
1080
1082
|
this.setupGitHooksAutomation();
|
|
1081
1083
|
|
|
1082
1084
|
// Dependency change automation
|
|
1083
|
-
this.setupDependencyAutomation
|
|
1085
|
+
if (typeof this.setupDependencyAutomation === 'function') {
|
|
1086
|
+
this.setupDependencyAutomation();
|
|
1087
|
+
}
|
|
1084
1088
|
|
|
1085
1089
|
// Performance monitoring automation
|
|
1086
|
-
this.setupPerformanceAutomation
|
|
1090
|
+
if (typeof this.setupPerformanceAutomation === 'function') {
|
|
1091
|
+
this.setupPerformanceAutomation();
|
|
1092
|
+
}
|
|
1087
1093
|
|
|
1088
1094
|
// Security monitoring automation
|
|
1089
|
-
this.setupSecurityAutomation
|
|
1095
|
+
if (typeof this.setupSecurityAutomation === 'function') {
|
|
1096
|
+
this.setupSecurityAutomation();
|
|
1097
|
+
}
|
|
1090
1098
|
}
|
|
1091
1099
|
|
|
1092
1100
|
/**
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs').promises;
|
|
9
9
|
const path = require('path');
|
|
10
|
-
|
|
10
|
+
// Handle chalk properly
|
|
11
|
+
const chalkModule = require('chalk');
|
|
12
|
+
const chalk = chalkModule.default || chalkModule;;
|
|
11
13
|
const crypto = require('crypto');
|
|
12
14
|
const WebSocket = require('ws');
|
|
13
15
|
const EventEmitter = require('events');
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
// Handle chalk properly
|
|
6
|
+
const chalkModule = require('chalk');
|
|
7
|
+
const chalk = chalkModule.default || chalkModule;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Production-Ready Todo Management System for Recoder Code
|
|
11
|
+
* Provides visual todo management with checkboxes and status tracking
|
|
12
|
+
*/
|
|
13
|
+
class TodoManager {
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.todoFile = options.todoFile || path.join(process.cwd(), '.recoder-todos.json');
|
|
16
|
+
this.todos = [];
|
|
17
|
+
this.currentUser = options.user || this.getCurrentUser();
|
|
18
|
+
this.loadTodos();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getCurrentUser() {
|
|
22
|
+
return process.env.USER || process.env.USERNAME || 'developer';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Load todos from file
|
|
27
|
+
*/
|
|
28
|
+
loadTodos() {
|
|
29
|
+
try {
|
|
30
|
+
if (fs.existsSync(this.todoFile)) {
|
|
31
|
+
const data = JSON.parse(fs.readFileSync(this.todoFile, 'utf8'));
|
|
32
|
+
this.todos = data.todos || [];
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.log(chalk.yellow(`⚠️ Could not load todos: ${error.message}`));
|
|
36
|
+
this.todos = [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Save todos to file
|
|
42
|
+
*/
|
|
43
|
+
saveTodos() {
|
|
44
|
+
try {
|
|
45
|
+
const data = {
|
|
46
|
+
todos: this.todos,
|
|
47
|
+
lastUpdated: new Date().toISOString(),
|
|
48
|
+
user: this.currentUser
|
|
49
|
+
};
|
|
50
|
+
fs.writeFileSync(this.todoFile, JSON.stringify(data, null, 2));
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.log(chalk.red(`❌ Could not save todos: ${error.message}`));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Add a new todo
|
|
58
|
+
*/
|
|
59
|
+
addTodo(content, priority = 'medium', category = 'general') {
|
|
60
|
+
const todo = {
|
|
61
|
+
id: this.generateId(),
|
|
62
|
+
content: content,
|
|
63
|
+
status: 'pending',
|
|
64
|
+
priority: priority,
|
|
65
|
+
category: category,
|
|
66
|
+
createdAt: new Date().toISOString(),
|
|
67
|
+
createdBy: this.currentUser,
|
|
68
|
+
updatedAt: new Date().toISOString()
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
this.todos.push(todo);
|
|
72
|
+
this.saveTodos();
|
|
73
|
+
return todo.id;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Update todo status
|
|
78
|
+
*/
|
|
79
|
+
updateTodoStatus(id, status) {
|
|
80
|
+
const todo = this.todos.find(t => t.id === id);
|
|
81
|
+
if (todo) {
|
|
82
|
+
todo.status = status;
|
|
83
|
+
todo.updatedAt = new Date().toISOString();
|
|
84
|
+
this.saveTodos();
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Mark todo as completed
|
|
92
|
+
*/
|
|
93
|
+
completeTodo(id) {
|
|
94
|
+
return this.updateTodoStatus(id, 'completed');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Mark todo as in progress
|
|
99
|
+
*/
|
|
100
|
+
startTodo(id) {
|
|
101
|
+
return this.updateTodoStatus(id, 'in_progress');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Delete a todo
|
|
106
|
+
*/
|
|
107
|
+
deleteTodo(id) {
|
|
108
|
+
const index = this.todos.findIndex(t => t.id === id);
|
|
109
|
+
if (index !== -1) {
|
|
110
|
+
this.todos.splice(index, 1);
|
|
111
|
+
this.saveTodos();
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Display todos with visual interface like shown
|
|
119
|
+
*/
|
|
120
|
+
displayTodos(showCompleted = true) {
|
|
121
|
+
console.log(chalk.cyan.bold('Update Todos'));
|
|
122
|
+
|
|
123
|
+
if (this.todos.length === 0) {
|
|
124
|
+
console.log(chalk.gray(' ⎿ No todos found. Add one with: /todo add <task>'));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Group todos by status
|
|
129
|
+
const pending = this.todos.filter(t => t.status === 'pending');
|
|
130
|
+
const inProgress = this.todos.filter(t => t.status === 'in_progress');
|
|
131
|
+
const completed = this.todos.filter(t => t.status === 'completed');
|
|
132
|
+
|
|
133
|
+
// Display in progress first
|
|
134
|
+
if (inProgress.length > 0) {
|
|
135
|
+
console.log(chalk.yellow(' ⎿ ◐ In Progress:'));
|
|
136
|
+
inProgress.forEach(todo => {
|
|
137
|
+
console.log(chalk.yellow(` ◐ ${todo.content}`));
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Display pending todos
|
|
142
|
+
if (pending.length > 0) {
|
|
143
|
+
console.log(chalk.white(' ⎿ ☐ Pending:'));
|
|
144
|
+
pending.forEach(todo => {
|
|
145
|
+
const priorityIcon = this.getPriorityIcon(todo.priority);
|
|
146
|
+
console.log(chalk.white(` ☐ ${priorityIcon} ${todo.content}`));
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Display completed todos if requested
|
|
151
|
+
if (showCompleted && completed.length > 0) {
|
|
152
|
+
console.log(chalk.green(' ⎿ ☒ Completed:'));
|
|
153
|
+
completed.forEach(todo => {
|
|
154
|
+
console.log(chalk.green(` ☒ ${todo.content}`));
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get priority icon
|
|
161
|
+
*/
|
|
162
|
+
getPriorityIcon(priority) {
|
|
163
|
+
switch (priority) {
|
|
164
|
+
case 'high': return '🔴';
|
|
165
|
+
case 'medium': return '🟡';
|
|
166
|
+
case 'low': return '🟢';
|
|
167
|
+
default: return '⚪';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Display todos by category
|
|
173
|
+
*/
|
|
174
|
+
displayTodosByCategory() {
|
|
175
|
+
const categories = {};
|
|
176
|
+
this.todos.forEach(todo => {
|
|
177
|
+
if (!categories[todo.category]) {
|
|
178
|
+
categories[todo.category] = [];
|
|
179
|
+
}
|
|
180
|
+
categories[todo.category].push(todo);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
Object.keys(categories).forEach(category => {
|
|
184
|
+
console.log(chalk.cyan.bold(`\n📋 ${category.toUpperCase()}`));
|
|
185
|
+
categories[category].forEach(todo => {
|
|
186
|
+
const statusIcon = this.getStatusIcon(todo.status);
|
|
187
|
+
const priorityIcon = this.getPriorityIcon(todo.priority);
|
|
188
|
+
console.log(` ${statusIcon} ${priorityIcon} ${todo.content}`);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get status icon
|
|
195
|
+
*/
|
|
196
|
+
getStatusIcon(status) {
|
|
197
|
+
switch (status) {
|
|
198
|
+
case 'completed': return chalk.green('☒');
|
|
199
|
+
case 'in_progress': return chalk.yellow('◐');
|
|
200
|
+
case 'pending': return chalk.white('☐');
|
|
201
|
+
default: return '⚪';
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Generate unique ID
|
|
207
|
+
*/
|
|
208
|
+
generateId() {
|
|
209
|
+
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get statistics
|
|
214
|
+
*/
|
|
215
|
+
getStats() {
|
|
216
|
+
const total = this.todos.length;
|
|
217
|
+
const completed = this.todos.filter(t => t.status === 'completed').length;
|
|
218
|
+
const inProgress = this.todos.filter(t => t.status === 'in_progress').length;
|
|
219
|
+
const pending = this.todos.filter(t => t.status === 'pending').length;
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
total,
|
|
223
|
+
completed,
|
|
224
|
+
inProgress,
|
|
225
|
+
pending,
|
|
226
|
+
completionRate: total > 0 ? Math.round((completed / total) * 100) : 0
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Display statistics
|
|
232
|
+
*/
|
|
233
|
+
displayStats() {
|
|
234
|
+
const stats = this.getStats();
|
|
235
|
+
console.log(chalk.cyan.bold('📊 Todo Statistics'));
|
|
236
|
+
console.log(chalk.white(`Total: ${stats.total}`));
|
|
237
|
+
console.log(chalk.green(`Completed: ${stats.completed}`));
|
|
238
|
+
console.log(chalk.yellow(`In Progress: ${stats.inProgress}`));
|
|
239
|
+
console.log(chalk.gray(`Pending: ${stats.pending}`));
|
|
240
|
+
console.log(chalk.blue(`Completion Rate: ${stats.completionRate}%`));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Clear completed todos
|
|
245
|
+
*/
|
|
246
|
+
clearCompleted() {
|
|
247
|
+
const beforeCount = this.todos.length;
|
|
248
|
+
this.todos = this.todos.filter(t => t.status !== 'completed');
|
|
249
|
+
const removedCount = beforeCount - this.todos.length;
|
|
250
|
+
|
|
251
|
+
if (removedCount > 0) {
|
|
252
|
+
this.saveTodos();
|
|
253
|
+
console.log(chalk.green(`✅ Cleared ${removedCount} completed todos`));
|
|
254
|
+
} else {
|
|
255
|
+
console.log(chalk.gray('No completed todos to clear'));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Search todos
|
|
261
|
+
*/
|
|
262
|
+
searchTodos(query) {
|
|
263
|
+
const results = this.todos.filter(todo =>
|
|
264
|
+
todo.content.toLowerCase().includes(query.toLowerCase()) ||
|
|
265
|
+
todo.category.toLowerCase().includes(query.toLowerCase())
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
if (results.length === 0) {
|
|
269
|
+
console.log(chalk.gray(`No todos found matching: ${query}`));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log(chalk.cyan.bold(`🔍 Search Results for "${query}":`));
|
|
274
|
+
results.forEach(todo => {
|
|
275
|
+
const statusIcon = this.getStatusIcon(todo.status);
|
|
276
|
+
const priorityIcon = this.getPriorityIcon(todo.priority);
|
|
277
|
+
console.log(` ${statusIcon} ${priorityIcon} ${todo.content}`);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Import todos from existing TodoWrite format
|
|
283
|
+
*/
|
|
284
|
+
importFromTodoWrite(todoWriteTodos) {
|
|
285
|
+
todoWriteTodos.forEach(todo => {
|
|
286
|
+
this.addTodo(todo.content, 'medium', 'imported');
|
|
287
|
+
if (todo.status !== 'pending') {
|
|
288
|
+
this.updateTodoStatus(this.todos[this.todos.length - 1].id, todo.status);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
console.log(chalk.green(`✅ Imported ${todoWriteTodos.length} todos`));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module.exports = TodoManager;
|
|
296
|
+
|
|
297
|
+
// CLI interface when run directly
|
|
298
|
+
if (require.main === module) {
|
|
299
|
+
const todoManager = new TodoManager();
|
|
300
|
+
const command = process.argv[2];
|
|
301
|
+
const args = process.argv.slice(3);
|
|
302
|
+
|
|
303
|
+
switch (command) {
|
|
304
|
+
case 'add':
|
|
305
|
+
if (args.length === 0) {
|
|
306
|
+
console.log('Usage: todo-manager.js add <content> [priority] [category]');
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
const content = args[0];
|
|
310
|
+
const priority = args[1] || 'medium';
|
|
311
|
+
const category = args[2] || 'general';
|
|
312
|
+
const id = todoManager.addTodo(content, priority, category);
|
|
313
|
+
console.log(chalk.green(`✅ Added todo: ${id}`));
|
|
314
|
+
break;
|
|
315
|
+
|
|
316
|
+
case 'list':
|
|
317
|
+
todoManager.displayTodos();
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case 'complete':
|
|
321
|
+
if (args.length === 0) {
|
|
322
|
+
console.log('Usage: todo-manager.js complete <id>');
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
if (todoManager.completeTodo(args[0])) {
|
|
326
|
+
console.log(chalk.green('✅ Todo marked as completed'));
|
|
327
|
+
} else {
|
|
328
|
+
console.log(chalk.red('❌ Todo not found'));
|
|
329
|
+
}
|
|
330
|
+
break;
|
|
331
|
+
|
|
332
|
+
case 'start':
|
|
333
|
+
if (args.length === 0) {
|
|
334
|
+
console.log('Usage: todo-manager.js start <id>');
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
if (todoManager.startTodo(args[0])) {
|
|
338
|
+
console.log(chalk.yellow('◐ Todo marked as in progress'));
|
|
339
|
+
} else {
|
|
340
|
+
console.log(chalk.red('❌ Todo not found'));
|
|
341
|
+
}
|
|
342
|
+
break;
|
|
343
|
+
|
|
344
|
+
case 'delete':
|
|
345
|
+
if (args.length === 0) {
|
|
346
|
+
console.log('Usage: todo-manager.js delete <id>');
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
if (todoManager.deleteTodo(args[0])) {
|
|
350
|
+
console.log(chalk.green('✅ Todo deleted'));
|
|
351
|
+
} else {
|
|
352
|
+
console.log(chalk.red('❌ Todo not found'));
|
|
353
|
+
}
|
|
354
|
+
break;
|
|
355
|
+
|
|
356
|
+
case 'stats':
|
|
357
|
+
todoManager.displayStats();
|
|
358
|
+
break;
|
|
359
|
+
|
|
360
|
+
case 'clear':
|
|
361
|
+
todoManager.clearCompleted();
|
|
362
|
+
break;
|
|
363
|
+
|
|
364
|
+
case 'search':
|
|
365
|
+
if (args.length === 0) {
|
|
366
|
+
console.log('Usage: todo-manager.js search <query>');
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
todoManager.searchTodos(args[0]);
|
|
370
|
+
break;
|
|
371
|
+
|
|
372
|
+
case 'categories':
|
|
373
|
+
todoManager.displayTodosByCategory();
|
|
374
|
+
break;
|
|
375
|
+
|
|
376
|
+
default:
|
|
377
|
+
console.log(chalk.cyan.bold('Recoder Code - Todo Manager'));
|
|
378
|
+
console.log('');
|
|
379
|
+
console.log('Usage:');
|
|
380
|
+
console.log(' todo-manager.js add <content> [priority] [category]');
|
|
381
|
+
console.log(' todo-manager.js list');
|
|
382
|
+
console.log(' todo-manager.js complete <id>');
|
|
383
|
+
console.log(' todo-manager.js start <id>');
|
|
384
|
+
console.log(' todo-manager.js delete <id>');
|
|
385
|
+
console.log(' todo-manager.js stats');
|
|
386
|
+
console.log(' todo-manager.js clear');
|
|
387
|
+
console.log(' todo-manager.js search <query>');
|
|
388
|
+
console.log(' todo-manager.js categories');
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
}
|