@task-mcp/cli 1.0.4 → 1.0.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.
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Interactive Mode for Task MCP CLI
3
+ * Zero-dependency implementation using Bun native capabilities
4
+ */
5
+
6
+ import { c, banner } from "./ansi.js";
7
+ import { dashboard } from "./commands/dashboard.js";
8
+ import { listTasksCmd, listProjectsCmd } from "./commands/list.js";
9
+ import * as readline from "node:readline";
10
+
11
+ interface MenuOption {
12
+ key: string;
13
+ label: string;
14
+ description?: string;
15
+ action: () => Promise<void> | void;
16
+ }
17
+
18
+ /**
19
+ * Simple prompt for user input
20
+ */
21
+ async function prompt(question: string): Promise<string> {
22
+ const rl = readline.createInterface({
23
+ input: process.stdin,
24
+ output: process.stdout,
25
+ });
26
+
27
+ return new Promise((resolve) => {
28
+ rl.question(question, (answer) => {
29
+ rl.close();
30
+ resolve(answer.trim());
31
+ });
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Display menu and get user selection
37
+ */
38
+ async function showMenu(title: string, options: MenuOption[]): Promise<void> {
39
+ console.clear();
40
+ console.log(banner("TASK MCP"));
41
+ console.log();
42
+ console.log(c.bold(c.cyan(title)));
43
+ console.log(c.dim("─".repeat(40)));
44
+ console.log();
45
+
46
+ for (const opt of options) {
47
+ const desc = opt.description ? c.dim(` - ${opt.description}`) : "";
48
+ console.log(` ${c.yellow(opt.key)} ${opt.label}${desc}`);
49
+ }
50
+
51
+ console.log();
52
+ const choice = await prompt(c.cyan("Select option: "));
53
+
54
+ const selected = options.find((o) => o.key.toLowerCase() === choice.toLowerCase());
55
+ if (selected) {
56
+ await selected.action();
57
+ } else if (choice !== "") {
58
+ console.log(c.error(`Invalid option: ${choice}`));
59
+ await sleep(1000);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Sleep utility
65
+ */
66
+ function sleep(ms: number): Promise<void> {
67
+ return new Promise((resolve) => setTimeout(resolve, ms));
68
+ }
69
+
70
+ /**
71
+ * Wait for any key press
72
+ */
73
+ async function waitForKey(message: string = "Press Enter to continue..."): Promise<void> {
74
+ await prompt(c.dim(message));
75
+ }
76
+
77
+ /**
78
+ * Main Menu
79
+ */
80
+ async function mainMenu(): Promise<boolean> {
81
+ const options: MenuOption[] = [
82
+ {
83
+ key: "d",
84
+ label: "Dashboard",
85
+ description: "View project dashboard",
86
+ action: async () => {
87
+ await dashboardMenu();
88
+ },
89
+ },
90
+ {
91
+ key: "p",
92
+ label: "Projects",
93
+ description: "List and manage projects",
94
+ action: async () => {
95
+ await projectsMenu();
96
+ },
97
+ },
98
+ {
99
+ key: "t",
100
+ label: "Tasks",
101
+ description: "List and filter tasks",
102
+ action: async () => {
103
+ await tasksMenu();
104
+ },
105
+ },
106
+ {
107
+ key: "a",
108
+ label: "Analysis",
109
+ description: "View analysis tools",
110
+ action: async () => {
111
+ await analysisMenu();
112
+ },
113
+ },
114
+ {
115
+ key: "q",
116
+ label: "Quick Actions",
117
+ description: "Common quick actions",
118
+ action: async () => {
119
+ await quickActionsMenu();
120
+ },
121
+ },
122
+ {
123
+ key: "x",
124
+ label: "Exit",
125
+ description: "Exit interactive mode",
126
+ action: () => {
127
+ console.log(c.dim("Goodbye!"));
128
+ process.exit(0);
129
+ },
130
+ },
131
+ ];
132
+
133
+ await showMenu("Main Menu", options);
134
+ return true;
135
+ }
136
+
137
+ /**
138
+ * Dashboard Menu
139
+ */
140
+ async function dashboardMenu(): Promise<void> {
141
+ console.clear();
142
+ console.log(c.bold(c.cyan("Dashboard")));
143
+ console.log(c.dim("─".repeat(40)));
144
+ console.log();
145
+
146
+ const projectId = await prompt(c.cyan("Project ID (Enter for all): "));
147
+
148
+ console.log();
149
+ await dashboard(projectId || undefined);
150
+ console.log();
151
+ await waitForKey();
152
+ }
153
+
154
+ /**
155
+ * Projects Menu
156
+ */
157
+ async function projectsMenu(): Promise<void> {
158
+ const options: MenuOption[] = [
159
+ {
160
+ key: "l",
161
+ label: "List Projects",
162
+ action: async () => {
163
+ console.clear();
164
+ await listProjectsCmd({ all: false });
165
+ console.log();
166
+ await waitForKey();
167
+ },
168
+ },
169
+ {
170
+ key: "a",
171
+ label: "List All (include archived)",
172
+ action: async () => {
173
+ console.clear();
174
+ await listProjectsCmd({ all: true });
175
+ console.log();
176
+ await waitForKey();
177
+ },
178
+ },
179
+ {
180
+ key: "b",
181
+ label: "Back to Main Menu",
182
+ action: () => {},
183
+ },
184
+ ];
185
+
186
+ await showMenu("Projects", options);
187
+ }
188
+
189
+ /**
190
+ * Tasks Menu
191
+ */
192
+ async function tasksMenu(): Promise<void> {
193
+ const options: MenuOption[] = [
194
+ {
195
+ key: "l",
196
+ label: "List All Tasks",
197
+ action: async () => {
198
+ console.clear();
199
+ await listTasksCmd({ all: false });
200
+ console.log();
201
+ await waitForKey();
202
+ },
203
+ },
204
+ {
205
+ key: "p",
206
+ label: "Filter by Priority",
207
+ action: async () => {
208
+ console.clear();
209
+ const priority = await prompt(c.cyan("Priority (critical/high/medium/low): "));
210
+ if (priority) {
211
+ await listTasksCmd({ priority });
212
+ }
213
+ console.log();
214
+ await waitForKey();
215
+ },
216
+ },
217
+ {
218
+ key: "s",
219
+ label: "Filter by Status",
220
+ action: async () => {
221
+ console.clear();
222
+ const status = await prompt(c.cyan("Status (pending/in_progress/blocked/completed): "));
223
+ if (status) {
224
+ await listTasksCmd({ status });
225
+ }
226
+ console.log();
227
+ await waitForKey();
228
+ },
229
+ },
230
+ {
231
+ key: "a",
232
+ label: "All (include completed)",
233
+ action: async () => {
234
+ console.clear();
235
+ await listTasksCmd({ all: true });
236
+ console.log();
237
+ await waitForKey();
238
+ },
239
+ },
240
+ {
241
+ key: "b",
242
+ label: "Back to Main Menu",
243
+ action: () => {},
244
+ },
245
+ ];
246
+
247
+ await showMenu("Tasks", options);
248
+ }
249
+
250
+ /**
251
+ * Analysis Menu
252
+ */
253
+ async function analysisMenu(): Promise<void> {
254
+ const options: MenuOption[] = [
255
+ {
256
+ key: "c",
257
+ label: "Complexity Analysis",
258
+ description: "View task complexity summary",
259
+ action: async () => {
260
+ console.clear();
261
+ console.log(c.yellow("Complexity Analysis"));
262
+ console.log(c.dim("Use MCP tools: get_complexity_summary, save_complexity_analysis"));
263
+ console.log();
264
+ console.log("Features:");
265
+ console.log(" - Score 1-10 complexity rating");
266
+ console.log(" - Factors: state_management, cross_cutting, etc.");
267
+ console.log(" - Suggested subtask count");
268
+ console.log();
269
+ await waitForKey();
270
+ },
271
+ },
272
+ {
273
+ key: "t",
274
+ label: "Tech Stack Analysis",
275
+ description: "View tech areas and risk",
276
+ action: async () => {
277
+ console.clear();
278
+ console.log(c.yellow("Tech Stack Analysis"));
279
+ console.log(c.dim("Use MCP tools: get_tech_stack_summary, save_tech_stack_analysis"));
280
+ console.log();
281
+ console.log("Features:");
282
+ console.log(" - Areas: schema, backend, frontend, infra, devops, test, docs");
283
+ console.log(" - Risk levels: low, medium, high, critical");
284
+ console.log(" - Breaking change detection");
285
+ console.log();
286
+ await waitForKey();
287
+ },
288
+ },
289
+ {
290
+ key: "r",
291
+ label: "Risk Analysis",
292
+ description: "Find high-risk tasks",
293
+ action: async () => {
294
+ console.clear();
295
+ console.log(c.yellow("Risk Analysis"));
296
+ console.log(c.dim("Use MCP tools: find_high_risk_tasks, suggest_safe_order"));
297
+ console.log();
298
+ console.log("Features:");
299
+ console.log(" - Identify high/critical risk tasks");
300
+ console.log(" - Suggest safe execution order");
301
+ console.log(" - Phase-based task grouping");
302
+ console.log();
303
+ await waitForKey();
304
+ },
305
+ },
306
+ {
307
+ key: "b",
308
+ label: "Back to Main Menu",
309
+ action: () => {},
310
+ },
311
+ ];
312
+
313
+ await showMenu("Analysis Tools", options);
314
+ }
315
+
316
+ /**
317
+ * Quick Actions Menu
318
+ */
319
+ async function quickActionsMenu(): Promise<void> {
320
+ const options: MenuOption[] = [
321
+ {
322
+ key: "t",
323
+ label: "Today's Tasks",
324
+ action: async () => {
325
+ console.clear();
326
+ console.log(c.bold(c.yellow("Today's Tasks")));
327
+ console.log(c.dim("Use MCP tool: view_today"));
328
+ console.log();
329
+ await waitForKey();
330
+ },
331
+ },
332
+ {
333
+ key: "w",
334
+ label: "This Week",
335
+ action: async () => {
336
+ console.clear();
337
+ console.log(c.bold(c.yellow("This Week's Tasks")));
338
+ console.log(c.dim("Use MCP tool: view_this_week"));
339
+ console.log();
340
+ await waitForKey();
341
+ },
342
+ },
343
+ {
344
+ key: "q",
345
+ label: "Quick Wins",
346
+ action: async () => {
347
+ console.clear();
348
+ console.log(c.bold(c.yellow("Quick Wins")));
349
+ console.log(c.dim("Use MCP tool: view_quick_wins"));
350
+ console.log();
351
+ await waitForKey();
352
+ },
353
+ },
354
+ {
355
+ key: "n",
356
+ label: "Next Task Suggestion",
357
+ action: async () => {
358
+ console.clear();
359
+ console.log(c.bold(c.yellow("Next Task Suggestion")));
360
+ console.log(c.dim("Use MCP tool: suggest_next_task"));
361
+ console.log();
362
+ await waitForKey();
363
+ },
364
+ },
365
+ {
366
+ key: "b",
367
+ label: "Back to Main Menu",
368
+ action: () => {},
369
+ },
370
+ ];
371
+
372
+ await showMenu("Quick Actions", options);
373
+ }
374
+
375
+ /**
376
+ * Start interactive mode
377
+ */
378
+ export async function startInteractive(): Promise<void> {
379
+ console.clear();
380
+ console.log(banner("TASK MCP"));
381
+ console.log();
382
+ console.log(c.bold("Welcome to Task MCP Interactive Mode"));
383
+ console.log(c.dim("Navigate using keyboard shortcuts"));
384
+ console.log();
385
+ await sleep(1000);
386
+
387
+ while (true) {
388
+ try {
389
+ await mainMenu();
390
+ } catch (error) {
391
+ if (error instanceof Error && error.message.includes("readline was closed")) {
392
+ // User pressed Ctrl+C or Ctrl+D
393
+ console.log();
394
+ console.log(c.dim("Goodbye!"));
395
+ process.exit(0);
396
+ }
397
+ throw error;
398
+ }
399
+ }
400
+ }