thinkncollab-shell 0.0.5 → 0.0.7

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.
@@ -0,0 +1,88 @@
1
+ /**
2
+ * src/commands/shell/notify.js — Send notifications to room
3
+ */
4
+ const chalk = require('chalk');
5
+
6
+ module.exports = {
7
+ name: 'notify',
8
+ description: 'Send a notification to the current room',
9
+ aliases: ['notif'],
10
+ requiresAuth: true,
11
+ requiresRoom: true,
12
+
13
+ // Usage:
14
+ // notify info <message>
15
+ // notify warn <message>
16
+ // notify error <message>
17
+ // notify success <message>
18
+ // notify <message> ← defaults to 'info'
19
+
20
+ async execute(args, shell) {
21
+ if (!args.length) {
22
+ console.log(chalk.yellow('Usage: notify <level> <message>'));
23
+ console.log(chalk.dim(' Levels: info | warn | error | success'));
24
+ console.log(chalk.dim(' Example: notify info deployment done'));
25
+ return;
26
+ }
27
+
28
+ const levels = ['info', 'warn', 'warning', 'error', 'success'];
29
+ let level, message;
30
+
31
+ if (levels.includes(args[0].toLowerCase())) {
32
+ level = args[0].toLowerCase() === 'warning' ? 'warn' : args[0].toLowerCase();
33
+ message = args.slice(1).join(' ');
34
+ } else {
35
+ level = 'info';
36
+ message = args.join(' ');
37
+ }
38
+
39
+ if (!message.trim()) {
40
+ console.log(chalk.red('❌ Message cannot be empty'));
41
+ return;
42
+ }
43
+
44
+ const levelColors = {
45
+ info: chalk.blue,
46
+ warn: chalk.yellow,
47
+ error: chalk.red,
48
+ success: chalk.green,
49
+ };
50
+ const levelIcons = {
51
+ info: 'ℹ️ ',
52
+ warn: '⚠️ ',
53
+ error: '❌',
54
+ success: '✅',
55
+ };
56
+
57
+ const color = levelColors[level] || chalk.blue;
58
+ const icon = levelIcons[level] || 'ℹ️ ';
59
+
60
+ try {
61
+ await shell.ws.sendNotification(
62
+ {
63
+ type: level,
64
+ title: `${level.charAt(0).toUpperCase() + level.slice(1)} from terminal`,
65
+ message: message,
66
+ data: {
67
+ sentBy: shell.api.getUser()?.name || 'unknown',
68
+ via: 'thinknsh',
69
+ },
70
+ },
71
+ 'room'
72
+ );
73
+
74
+ // Also push to local notification window
75
+ shell.pushNotification({
76
+ type: 'notification',
77
+ level: level,
78
+ title: `Notify — ${level}`,
79
+ message: message,
80
+ });
81
+
82
+ console.log(color(`${icon} Notification sent → [${level}] ${message}`));
83
+
84
+ } catch (err) {
85
+ console.log(chalk.red(`❌ Failed to send notification: ${err.message}`));
86
+ }
87
+ }
88
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * src/commands/tasks/complete.js — Mark your task assignment as completed
3
+ */
4
+ const chalk = require('chalk');
5
+
6
+ module.exports = {
7
+ name: 'complete',
8
+ description: 'Mark a task as completed',
9
+ aliases: ['done', 'finish', 'complete'],
10
+ requiresAuth: true,
11
+
12
+ async execute(args, shell) {
13
+ if (!args[0]) {
14
+ console.log(chalk.red('Usage: complete <task-id>'));
15
+ return;
16
+ }
17
+
18
+ const taskId = args[0];
19
+
20
+ try {
21
+ const result = await shell.api._request('POST', `/thinknsh/${taskId}/cli/complete`);
22
+
23
+ console.log(chalk.green(`✅ Task completed: "${result.task?.title || taskId}"`));
24
+
25
+ if (result.allCompleted)
26
+ console.log(chalk.green(' 🎉 All assignees have completed this task!'));
27
+
28
+ // Push to notification window
29
+ shell.pushNotification({
30
+ type: 'notification',
31
+ level: 'success',
32
+ title: 'Task Completed',
33
+ message: result.task?.title || taskId,
34
+ });
35
+
36
+ } catch (err) {
37
+ if (err.message?.includes('403'))
38
+ console.log(chalk.red('❌ You are not assigned to this task'));
39
+ else if (err.message?.includes('404'))
40
+ console.log(chalk.red('❌ Task not found'));
41
+ else if (err.message?.includes('already'))
42
+ console.log(chalk.yellow('⚠️ Task already completed'));
43
+ else
44
+ console.log(chalk.red(`❌ ${err.message}`));
45
+ }
46
+ }
47
+ };
@@ -0,0 +1,101 @@
1
+ /**
2
+ * src/commands/tasks/create-task.js — Create a new task interactively
3
+ */
4
+ const chalk = require('chalk');
5
+
6
+ module.exports = {
7
+ name: 'create-task',
8
+ description: 'Create a new task in current room',
9
+ aliases: ['newtask', 'addtask'],
10
+ requiresAuth: true,
11
+ requiresRoom: true,
12
+
13
+ async execute(args, shell) {
14
+ const { input, select, confirm } = require('@inquirer/prompts');
15
+ const roomId = shell.ws.getCurrentRoom();
16
+
17
+ try {
18
+ // Fetch columns of current room's board
19
+ const { columns } = await shell.api._request('GET', `/rooms/${roomId}/cli/columns`);
20
+ if (!columns?.length) {
21
+ console.log(chalk.red('❌ No columns found in this room\'s board'));
22
+ return;
23
+ }
24
+
25
+ console.log(chalk.cyan('\n // create-task\n'));
26
+
27
+ const title = await input({
28
+ message: 'Task title:',
29
+ validate: v => v.trim().length >= 3 || 'Min 3 characters'
30
+ });
31
+
32
+ const description = await input({ message: 'Description (optional):' });
33
+
34
+ const columnId = await select({
35
+ message: 'Column:',
36
+ choices: columns.map(c => ({ name: c.title, value: c._id }))
37
+ });
38
+
39
+ const priority = await select({
40
+ message: 'Priority:',
41
+ choices: [
42
+ { name: 'low', value: 'low' },
43
+ { name: 'medium', value: 'medium' },
44
+ { name: 'high', value: 'high' },
45
+ ],
46
+ default: 'medium'
47
+ });
48
+
49
+ const categories = [
50
+ 'Bugs', 'Feature Requests', 'Improvements / Enhancements',
51
+ 'Production Critical Issues', 'Security Issues', 'Performance Issues',
52
+ 'UI/UX Issues', 'Testing & QA', 'DevOps & Infrastructure',
53
+ 'Refactoring & Code Quality', 'Documentation', 'Other'
54
+ ];
55
+ const category = await select({
56
+ message: 'Category:',
57
+ choices: categories.map(c => ({ name: c, value: c }))
58
+ });
59
+
60
+ const dueDateStr = await input({
61
+ message: 'Due date (YYYY-MM-DD, optional):',
62
+ validate: v => {
63
+ if (!v) return true;
64
+ return !isNaN(Date.parse(v)) || 'Invalid date format';
65
+ }
66
+ });
67
+
68
+ const confirmed = await confirm({ message: 'Create task?', default: true });
69
+ if (!confirmed) { console.log(chalk.dim(' Cancelled.')); return; }
70
+
71
+ const payload = {
72
+ title: title.trim(),
73
+ description: description.trim() || undefined,
74
+ columnId,
75
+ priority,
76
+ category: category === 'Other' ? undefined : category,
77
+ dueDate: dueDateStr ? new Date(dueDateStr).toISOString() : undefined,
78
+ source: 'cli',
79
+ };
80
+
81
+ const result = await shell.api._request('POST', `/${roomId}/cli/tasks`, payload);
82
+
83
+ console.log(chalk.green(`\n✅ Task created: "${result.task.title}"`));
84
+ console.log(chalk.dim(` ID: ${result.task._id}`));
85
+ console.log(chalk.dim(` Column: ${result.task.columnId?.title || columnId}\n`));
86
+
87
+ shell.pushNotification({
88
+ type: 'notification',
89
+ level: 'success',
90
+ title: 'Task Created',
91
+ message: result.task.title,
92
+ });
93
+
94
+ } catch (err) {
95
+ if (err.message?.includes('403'))
96
+ console.log(chalk.red('❌ You are not a member of this room'));
97
+ else
98
+ console.log(chalk.red(`❌ ${err.message}`));
99
+ }
100
+ }
101
+ };
File without changes
@@ -0,0 +1,61 @@
1
+ // src/commands/tasks/task-status.js
2
+ const chalk = require('chalk');
3
+
4
+ const STATUS_MAP = {
5
+ accept: 'accepted',
6
+ start: 'inprogress',
7
+ complete: 'completed',
8
+ reject: 'rejected',
9
+ done: 'completed',
10
+ };
11
+
12
+ module.exports = {
13
+ name: 'task-status',
14
+ description: 'Update your task status',
15
+ aliases: ['accept', 'start', 'complete', 'reject', 'done'],
16
+ requiresAuth: true,
17
+ requiresRoom: true,
18
+
19
+ async execute(args, shell) {
20
+ const roomId = shell.ws.getCurrentRoom();
21
+ const taskId = args[0];
22
+ // invoked as 'accept <id>', 'start <id>', etc.
23
+ const verb = shell._lastCommand || 'task-status';
24
+ const status = STATUS_MAP[verb] || args[1];
25
+
26
+ if (!taskId) {
27
+ console.log(chalk.yellow(' Usage: accept <taskId> | start <taskId> | complete <taskId>'));
28
+ return;
29
+ }
30
+ if (!status) {
31
+ console.log(chalk.yellow(' Usage: task-status <taskId> <accepted|inprogress|completed|rejected>'));
32
+ return;
33
+ }
34
+
35
+ const statusColors = {
36
+ accepted: chalk.green,
37
+ inprogress: chalk.blue,
38
+ completed: chalk.green,
39
+ rejected: chalk.red,
40
+ };
41
+ const col = statusColors[status] || chalk.white;
42
+
43
+ try {
44
+ const result = await shell.api._request(
45
+ 'POST',
46
+ `/thinknsh/${roomId}/tasks/${taskId}/status`,
47
+ { status }
48
+ );
49
+
50
+ console.log(col(`\n ✓ Task "${result.taskTitle}" → ${status}`));
51
+ if (result.allCompleted) {
52
+ console.log(chalk.green(' 🎉 All assignees done — task fully completed!\n'));
53
+ } else {
54
+ console.log('');
55
+ }
56
+
57
+ } catch (err) {
58
+ console.log(chalk.red(`\n ❌ ${err.message}\n`));
59
+ }
60
+ }
61
+ };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * src/commands/tasks/task.js — View single task detail
3
+ */
4
+ const chalk = require('chalk');
5
+
6
+ module.exports = {
7
+ name: 'task',
8
+ description: 'View task details by ID',
9
+ aliases: ['t'],
10
+ requiresAuth: true,
11
+
12
+ async execute(args, shell) {
13
+ if (!args[0]) {
14
+ console.log(chalk.red('Usage: task <task-id>'));
15
+ return;
16
+ }
17
+
18
+ const taskId = args[0];
19
+
20
+ try {
21
+ const task = await shell.api._request('GET', `/thinknsh/${taskId}/cli`);
22
+
23
+ const stColors = {
24
+ pending: chalk.yellow,
25
+ inprogress: chalk.blue,
26
+ review: chalk.magenta,
27
+ completed: chalk.green,
28
+ reopened: chalk.cyan,
29
+ };
30
+ const prColors = { high: chalk.red, medium: chalk.yellow, low: chalk.green };
31
+ const stColor = stColors[task.status] || chalk.white;
32
+ const prColor = prColors[task.priority] || chalk.white;
33
+
34
+ console.log(chalk.cyan('\n ─── Task ────────────────────────────────────────'));
35
+ console.log(` ${chalk.dim('id')} ${chalk.dim(task._id)}`);
36
+ console.log(` ${chalk.dim('title')} ${chalk.bold(task.title)}`);
37
+ console.log(` ${chalk.dim('status')} ${stColor(task.status)}`);
38
+ console.log(` ${chalk.dim('priority')} ${prColor(task.priority)}`);
39
+ if (task.category)
40
+ console.log(` ${chalk.dim('category')} ${chalk.dim(task.category)}`);
41
+ if (task.dueDate)
42
+ console.log(` ${chalk.dim('due')} ${chalk.yellow(new Date(task.dueDate).toLocaleDateString())}`);
43
+ if (task.description) {
44
+ console.log(chalk.dim('\n description:'));
45
+ task.description.split('\n').forEach(l => console.log(` ${chalk.white(l)}`));
46
+ }
47
+
48
+ if (task.assignedTo?.length) {
49
+ console.log(chalk.dim('\n assignees:'));
50
+ task.assignedTo.forEach(a => {
51
+ const name = a.userId?.name || 'Unknown';
52
+ const stBadge = a.status === 'completed' ? chalk.green('✓') :
53
+ a.status === 'accepted' ? chalk.blue('●') :
54
+ a.status === 'rejected' ? chalk.red('✗') : chalk.dim('○');
55
+ console.log(` ${stBadge} ${name} ${chalk.dim(`[${a.status}]`)}`);
56
+ });
57
+ }
58
+
59
+ if (task.columnId?.name || task.columnName)
60
+ console.log(`\n ${chalk.dim('column')} ${task.columnId?.name || task.columnName}`);
61
+ if (task.boardName)
62
+ console.log(` ${chalk.dim('board')} ${task.boardName}`);
63
+
64
+ console.log(chalk.cyan(' ─────────────────────────────────────────────────\n'));
65
+
66
+ } catch (err) {
67
+ if (err.message?.includes('403'))
68
+ console.log(chalk.red('❌ Access denied — you are not a member of this room'));
69
+ else if (err.message?.includes('404'))
70
+ console.log(chalk.red('❌ Task not found'));
71
+ else
72
+ console.log(chalk.red(`❌ ${err.message}`));
73
+ }
74
+ }
75
+ };
@@ -0,0 +1,183 @@
1
+ /**
2
+ * src/commands/tasks/tasks.js — List my tasks + suggestions in current room
3
+ */
4
+ const chalk = require('chalk');
5
+
6
+ module.exports = {
7
+ name: 'tasks',
8
+ description: 'List your tasks + suggested tasks in the current room',
9
+ aliases: ['mytasks', 'my-tasks'],
10
+ requiresAuth: true,
11
+ requiresRoom: true,
12
+
13
+ async execute(args, shell) {
14
+ const user = shell.api.getUser();
15
+ const roomId = shell.ws.getCurrentRoom();
16
+
17
+ if (!user) { console.log(chalk.red('❌ Please login first')); return; }
18
+ if (!roomId) { console.log(chalk.yellow('⚠️ Join a room first: join <room-id>')); return; }
19
+
20
+ // ── Flags ────────────────────────────────────────────────────────────
21
+ const showAll = args.includes('--all') || args.includes('-a');
22
+ const noSuggestions = args.includes('--no-suggest') || args.includes('-ns');
23
+ const filterStatus = args.find(a => ['pending','inprogress','review','completed','reopened'].includes(a));
24
+ const topN = args.find(a => /^\d+$/.test(a)) || 5;
25
+
26
+ // ── Color maps ───────────────────────────────────────────────────────
27
+ const stColors = {
28
+ pending: chalk.yellow,
29
+ inprogress: chalk.blue,
30
+ review: chalk.magenta,
31
+ completed: chalk.green,
32
+ reopened: chalk.cyan,
33
+ };
34
+ const prColors = {
35
+ critical: chalk.red,
36
+ high: chalk.hex('#FF6B35'),
37
+ medium: chalk.yellow,
38
+ low: chalk.green,
39
+ };
40
+ const scoreColor = s => s >= 80 ? chalk.red(s) : s >= 60 ? chalk.yellow(s) : chalk.green(s);
41
+ const plColor = pl => ({
42
+ CRITICAL: chalk.red,
43
+ HIGH: chalk.hex('#FF6B35'),
44
+ MEDIUM: chalk.yellow,
45
+ LOW: chalk.green,
46
+ 'VERY LOW': chalk.dim,
47
+ }[pl] || chalk.white);
48
+
49
+ // ── Helpers ──────────────────────────────────────────────────────────
50
+ const fmtDue = dueDate => {
51
+ if (!dueDate) return '';
52
+ const d = new Date(dueDate);
53
+ const days = Math.ceil((d - new Date().setHours(0,0,0,0)) / 86400000);
54
+ const str = d.toLocaleDateString('en-IN', { day:'2-digit', month:'short' });
55
+ if (days < 0) return chalk.red(` due:${str}(!)`);
56
+ if (days === 0) return chalk.yellow(` due:TODAY`);
57
+ if (days === 1) return chalk.yellow(` due:${str}`);
58
+ return chalk.dim(` due:${str}`);
59
+ };
60
+
61
+ const divider = (color = chalk.cyan) => color(' ' + '─'.repeat(58));
62
+ const subline = (label, val) =>
63
+ ` ${chalk.dim(label+':')} ${chalk.dim(val)}`;
64
+
65
+ // ── Fetch ────────────────────────────────────────────────────────────
66
+ process.stdout.write(chalk.dim(' Fetching tasks...\r'));
67
+
68
+ let result;
69
+ try {
70
+ result = await shell.api._request('GET', `/thinknsh/${roomId}/tasks?n=${topN}`);
71
+ } catch (err) {
72
+ console.log(chalk.red(`\n ❌ ${err.message}\n`));
73
+ return;
74
+ }
75
+
76
+ process.stdout.write(' '.repeat(30) + '\r'); // clear spinner line
77
+
78
+ // ════════════════════════════════════════════════════════════════════
79
+ // MY TASKS
80
+ // ════════════════════════════════════════════════════════════════════
81
+ let tasks = result.tasks || [];
82
+
83
+ if (filterStatus) tasks = tasks.filter(t => t.status === filterStatus);
84
+ if (!showAll) tasks = tasks.filter(t => !['completed','done'].includes(t.status));
85
+
86
+ console.log(divider());
87
+ console.log(chalk.cyan(` MY TASKS`) + chalk.dim(` (${tasks.length} active${result.total > tasks.length ? ` of ${result.total}` : ''})`));
88
+ console.log(divider());
89
+
90
+ if (!tasks.length) {
91
+ console.log(chalk.dim('\n No assigned tasks.\n'));
92
+ } else {
93
+ tasks.forEach((task, i) => {
94
+ const sc = stColors[task.status] || chalk.white;
95
+ const pc = prColors[task.priority] || chalk.white;
96
+
97
+ const num = chalk.dim(` ${String(i+1).padStart(2)}.`);
98
+ const status = sc(`[${(task.status || 'unknown').padEnd(10)}]`);
99
+ const title = task.status === 'completed'
100
+ ? chalk.dim(task.title)
101
+ : chalk.white(task.title);
102
+ const due = fmtDue(task.dueDate);
103
+ const myStatus = task.myStatus && task.myStatus !== task.status
104
+ ? chalk.dim(` (${task.myStatus})`)
105
+ : '';
106
+ const col = task.column ? chalk.dim(` [${task.column}]`) : '';
107
+
108
+ console.log(`\n${num} ${status} ${title}`);
109
+ console.log(subline('id', task._id) +
110
+ ` ${chalk.dim('priority:')} ${pc(task.priority)}` +
111
+ due + myStatus + col);
112
+
113
+ if (task.description) {
114
+ const desc = task.description.length > 90
115
+ ? task.description.slice(0, 87) + '...'
116
+ : task.description;
117
+ console.log(` ${chalk.dim(desc)}`);
118
+ }
119
+ });
120
+ }
121
+
122
+ // ════════════════════════════════════════════════════════════════════
123
+ // SUGGESTIONS
124
+ // ════════════════════════════════════════════════════════════════════
125
+ if (!noSuggestions) {
126
+ const suggestions = result.suggestions?.tasks || [];
127
+ const insights = result.suggestions?.insights || {};
128
+ const meta = result.suggestions?.metadata || {};
129
+
130
+ console.log('\n' + divider(chalk.magenta));
131
+ console.log(chalk.magenta(` SUGGESTED FOR YOU`) +
132
+ chalk.dim(` (top ${suggestions.length}${meta.totalScored ? ` of ${meta.totalScored} available` : ''})`));
133
+ console.log(divider(chalk.magenta));
134
+
135
+ if (!suggestions.length) {
136
+ console.log(chalk.dim('\n No suggestions — all tasks are assigned.\n'));
137
+ } else {
138
+ suggestions.forEach((task, i) => {
139
+ const pc = prColors[task.priority] || chalk.white;
140
+ const plc = plColor(task.priorityLevel);
141
+ const due = fmtDue(task.dueDate);
142
+ const col = task.column ? chalk.dim(` [${task.column}]`) : '';
143
+ const assignees = task.assigneeCount
144
+ ? chalk.dim(` · ${task.assigneeCount} assigned`)
145
+ : chalk.dim(' · unassigned');
146
+ const urgentTag = task.isOverdue
147
+ ? chalk.red(' ⚠ OVERDUE')
148
+ : task.isUrgent ? chalk.yellow(' ⏰ URGENT') : '';
149
+
150
+ const num = chalk.dim(` ${String(i+1).padStart(2)}.`);
151
+ const score = `[score:${scoreColor(task.score)}]`;
152
+ const level = plc(`[${task.priorityLevel}]`);
153
+
154
+ console.log(`\n${num} ${score} ${level} ${chalk.white(task.title)}${urgentTag}`);
155
+ console.log(subline('id', task._id) +
156
+ ` ${chalk.dim('priority:')} ${pc(task.priority)}` +
157
+ due + col + assignees);
158
+
159
+ if (task.priorityReason) {
160
+ console.log(` ${chalk.dim('↳ ' + task.priorityReason)}`);
161
+ }
162
+ });
163
+
164
+ // ── Insights bar ─────────────────────────────────────────────
165
+ console.log('\n' + divider(chalk.dim));
166
+ if (insights.focusRecommendation) {
167
+ console.log(` ${chalk.magenta('💡')} ${chalk.white(insights.focusRecommendation)}`);
168
+ }
169
+ if (insights.estimatedTime) {
170
+ console.log(` ${chalk.dim('⏱ Est. time:')} ${chalk.white(insights.estimatedTime)}`);
171
+ }
172
+ if (meta.scoreRange) {
173
+ console.log(` ${chalk.dim('📊 Score range:')} ${chalk.white(`${meta.scoreRange.min}–${meta.scoreRange.max}`)} ${chalk.dim('avg:')} ${chalk.white(meta.scoreRange.avg)}`);
174
+ }
175
+ }
176
+ }
177
+
178
+ // ── Footer ───────────────────────────────────────────────────────────
179
+ console.log('\n' + divider());
180
+ console.log(chalk.dim(' task <id> · complete <id> · tasks --all · tasks 10 · tasks --no-suggest'));
181
+ console.log(divider() + '\n');
182
+ }
183
+ };