@task-mcp/cli 1.0.4 → 1.0.6
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 +127 -35
- package/package.json +5 -1
- package/src/__tests__/ansi.test.ts +221 -0
- package/src/__tests__/index.test.ts +140 -0
- package/src/__tests__/storage.test.ts +271 -0
- package/src/ansi.ts +1 -14
- package/src/commands/dashboard.ts +371 -40
- package/src/commands/inbox.ts +267 -0
- package/src/commands/list.ts +1 -1
- package/src/index.ts +125 -4
- package/src/interactive.ts +400 -0
- package/src/storage.ts +355 -28
|
@@ -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
|
+
}
|