@task-mcp/cli 1.0.20 → 1.0.22
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 +3 -1
- package/dist/index.js +818 -0
- package/package.json +7 -5
- package/src/__tests__/ansi.test.ts +0 -221
- package/src/__tests__/dashboard.test.ts +0 -226
- package/src/__tests__/inbox.test.ts +0 -307
- package/src/__tests__/index.test.ts +0 -140
- package/src/__tests__/list.test.ts +0 -347
- package/src/__tests__/storage.test.ts +0 -271
- package/src/ansi.ts +0 -50
- package/src/commands/dashboard.ts +0 -92
- package/src/commands/inbox.ts +0 -229
- package/src/commands/list.ts +0 -106
- package/src/constants.ts +0 -59
- package/src/index.ts +0 -277
- package/src/interactive.ts +0 -254
- package/src/storage.ts +0 -221
package/dist/index.js
ADDED
|
@@ -0,0 +1,818 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// src/ansi.ts
|
|
5
|
+
import {
|
|
6
|
+
color,
|
|
7
|
+
style,
|
|
8
|
+
styled,
|
|
9
|
+
c,
|
|
10
|
+
BOX,
|
|
11
|
+
box,
|
|
12
|
+
drawBox,
|
|
13
|
+
hline,
|
|
14
|
+
stripAnsi,
|
|
15
|
+
displayWidth,
|
|
16
|
+
visibleLength,
|
|
17
|
+
pad,
|
|
18
|
+
padEnd,
|
|
19
|
+
padStart,
|
|
20
|
+
center,
|
|
21
|
+
truncateStr,
|
|
22
|
+
progressBar,
|
|
23
|
+
sideBySide,
|
|
24
|
+
sideBySideArrays,
|
|
25
|
+
table,
|
|
26
|
+
renderTable,
|
|
27
|
+
statusColors,
|
|
28
|
+
statusIcons,
|
|
29
|
+
icons,
|
|
30
|
+
formatStatus,
|
|
31
|
+
priorityColors,
|
|
32
|
+
formatPriority,
|
|
33
|
+
formatDependencies,
|
|
34
|
+
banner
|
|
35
|
+
} from "@task-mcp/shared";
|
|
36
|
+
import { truncateStr as truncateStr2 } from "@task-mcp/shared";
|
|
37
|
+
|
|
38
|
+
// src/commands/dashboard.ts
|
|
39
|
+
import {
|
|
40
|
+
renderGlobalDashboard,
|
|
41
|
+
renderWorkspaceDashboard
|
|
42
|
+
} from "@task-mcp/shared";
|
|
43
|
+
|
|
44
|
+
// src/storage.ts
|
|
45
|
+
import {
|
|
46
|
+
TaskStore,
|
|
47
|
+
InboxStore,
|
|
48
|
+
StateStore
|
|
49
|
+
} from "@task-mcp/mcp-server/storage";
|
|
50
|
+
var _taskStore = null;
|
|
51
|
+
var _inboxStore = null;
|
|
52
|
+
var _stateStore = null;
|
|
53
|
+
function getTaskStore() {
|
|
54
|
+
if (!_taskStore) {
|
|
55
|
+
_taskStore = new TaskStore;
|
|
56
|
+
}
|
|
57
|
+
return _taskStore;
|
|
58
|
+
}
|
|
59
|
+
function getInboxStore() {
|
|
60
|
+
if (!_inboxStore) {
|
|
61
|
+
_inboxStore = new InboxStore;
|
|
62
|
+
}
|
|
63
|
+
return _inboxStore;
|
|
64
|
+
}
|
|
65
|
+
function getStateStore() {
|
|
66
|
+
if (!_stateStore) {
|
|
67
|
+
_stateStore = new StateStore;
|
|
68
|
+
}
|
|
69
|
+
return _stateStore;
|
|
70
|
+
}
|
|
71
|
+
function getCurrentWorkspace() {
|
|
72
|
+
return getTaskStore().currentWorkspace;
|
|
73
|
+
}
|
|
74
|
+
async function listWorkspaces() {
|
|
75
|
+
const taskStore = getTaskStore();
|
|
76
|
+
const workspaceNames = await taskStore.listWorkspaces();
|
|
77
|
+
const workspaces = [];
|
|
78
|
+
for (const name of workspaceNames) {
|
|
79
|
+
const tasks = await taskStore.list({ workspaces: [name] });
|
|
80
|
+
workspaces.push({
|
|
81
|
+
name,
|
|
82
|
+
taskCount: tasks.length,
|
|
83
|
+
completedCount: tasks.filter((t) => t.status === "completed").length
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return workspaces;
|
|
87
|
+
}
|
|
88
|
+
async function listTasks() {
|
|
89
|
+
return getTaskStore().list();
|
|
90
|
+
}
|
|
91
|
+
async function listAllTasks() {
|
|
92
|
+
return getTaskStore().listAll();
|
|
93
|
+
}
|
|
94
|
+
async function listInboxItems(status) {
|
|
95
|
+
return getInboxStore().list({ status });
|
|
96
|
+
}
|
|
97
|
+
async function getActiveTag() {
|
|
98
|
+
return getStateStore().getActiveTag();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/commands/dashboard.ts
|
|
102
|
+
async function dashboard(workspaceName) {
|
|
103
|
+
const workspaces = await listWorkspaces();
|
|
104
|
+
const activeTag = await getActiveTag();
|
|
105
|
+
const currentWorkspace = getCurrentWorkspace();
|
|
106
|
+
if (workspaces.length === 0) {
|
|
107
|
+
console.log(c.yellow("No tasks found. Create a task first using the MCP server."));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
let selectedWorkspace;
|
|
111
|
+
let tasks;
|
|
112
|
+
if (workspaceName) {
|
|
113
|
+
selectedWorkspace = workspaces.find((w) => w.name === workspaceName || w.name.startsWith(workspaceName));
|
|
114
|
+
if (!selectedWorkspace) {
|
|
115
|
+
console.log(c.error(`Workspace not found: ${workspaceName}`));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
tasks = await listAllTasks();
|
|
119
|
+
tasks = tasks.filter((t) => t.workspace === selectedWorkspace.name);
|
|
120
|
+
} else if (workspaces.length === 1) {
|
|
121
|
+
selectedWorkspace = workspaces[0];
|
|
122
|
+
tasks = await listTasks();
|
|
123
|
+
} else {
|
|
124
|
+
tasks = await listAllTasks();
|
|
125
|
+
}
|
|
126
|
+
const inboxItems = await listInboxItems("pending");
|
|
127
|
+
const allTasksForLookup = await listAllTasks();
|
|
128
|
+
const tasksByWorkspace = new Map;
|
|
129
|
+
for (const task of allTasksForLookup) {
|
|
130
|
+
const workspaceTasks = tasksByWorkspace.get(task.workspace) ?? [];
|
|
131
|
+
workspaceTasks.push(task);
|
|
132
|
+
tasksByWorkspace.set(task.workspace, workspaceTasks);
|
|
133
|
+
}
|
|
134
|
+
const getWorkspaceTasks = (ws) => tasksByWorkspace.get(ws) ?? [];
|
|
135
|
+
let output;
|
|
136
|
+
if (selectedWorkspace) {
|
|
137
|
+
output = renderWorkspaceDashboard(selectedWorkspace.name, tasks, { version: VERSION, activeTag });
|
|
138
|
+
} else {
|
|
139
|
+
output = renderGlobalDashboard(workspaces, tasks, inboxItems, getWorkspaceTasks, { version: VERSION, activeTag });
|
|
140
|
+
}
|
|
141
|
+
console.log(output);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/constants.ts
|
|
145
|
+
var MENU_SEPARATOR_WIDTH = 40;
|
|
146
|
+
var LIST_SEPARATOR_WIDTH = 50;
|
|
147
|
+
var ERROR_MESSAGE_DELAY_MS = 1000;
|
|
148
|
+
var WELCOME_SCREEN_DELAY_MS = 1000;
|
|
149
|
+
var COLUMN_WIDTH_ID = 6;
|
|
150
|
+
var COLUMN_WIDTH_TITLE = 45;
|
|
151
|
+
var COLUMN_WIDTH_PROJECT_NAME = 30;
|
|
152
|
+
var COLUMN_WIDTH_STATUS = 14;
|
|
153
|
+
var COLUMN_WIDTH_PRIORITY = 10;
|
|
154
|
+
var COLUMN_WIDTH_DATE = 12;
|
|
155
|
+
var INBOX_PROMOTE_TITLE_MAX_LENGTH = 100;
|
|
156
|
+
var INBOX_DISCARD_PREVIEW_LENGTH = 50;
|
|
157
|
+
var ID_DISPLAY_LENGTH = 4;
|
|
158
|
+
|
|
159
|
+
// src/commands/list.ts
|
|
160
|
+
function formatDate(dateStr) {
|
|
161
|
+
if (!dateStr)
|
|
162
|
+
return c.gray("-");
|
|
163
|
+
const date = new Date(dateStr);
|
|
164
|
+
return date.toLocaleDateString();
|
|
165
|
+
}
|
|
166
|
+
async function listTasksCmd(options) {
|
|
167
|
+
let tasks;
|
|
168
|
+
if (options.workspace) {
|
|
169
|
+
const allTasks = await listAllTasks();
|
|
170
|
+
tasks = allTasks.filter((t) => t.workspace === options.workspace || t.workspace.startsWith(options.workspace));
|
|
171
|
+
} else {
|
|
172
|
+
tasks = await listTasks();
|
|
173
|
+
}
|
|
174
|
+
if (options.status) {
|
|
175
|
+
const statuses = options.status.split(",");
|
|
176
|
+
tasks = tasks.filter((t) => statuses.includes(t.status));
|
|
177
|
+
} else if (!options.all) {
|
|
178
|
+
tasks = tasks.filter((t) => t.status !== "completed" && t.status !== "cancelled");
|
|
179
|
+
}
|
|
180
|
+
if (options.priority) {
|
|
181
|
+
const priorities = options.priority.split(",");
|
|
182
|
+
tasks = tasks.filter((t) => priorities.includes(t.priority));
|
|
183
|
+
}
|
|
184
|
+
if (tasks.length === 0) {
|
|
185
|
+
console.log(c.gray("No tasks found."));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const currentWorkspace = getCurrentWorkspace();
|
|
189
|
+
console.log(c.bold(`
|
|
190
|
+
Tasks (${tasks.length}) - ${c.cyan(currentWorkspace)}
|
|
191
|
+
`));
|
|
192
|
+
const columns = [
|
|
193
|
+
{ header: "ID", key: "id", width: COLUMN_WIDTH_ID, format: (v) => c.cyan(String(v).slice(0, ID_DISPLAY_LENGTH)) },
|
|
194
|
+
{ header: "Title", key: "title", width: COLUMN_WIDTH_TITLE },
|
|
195
|
+
{ header: "Status", key: "status", width: COLUMN_WIDTH_STATUS, format: (v) => formatStatus(v) },
|
|
196
|
+
{ header: "Priority", key: "priority", width: COLUMN_WIDTH_PRIORITY, format: (v) => formatPriority(v) },
|
|
197
|
+
{ header: "Due", key: "dueDate", width: COLUMN_WIDTH_DATE, format: (v) => formatDate(v) }
|
|
198
|
+
];
|
|
199
|
+
console.log(table(tasks, columns));
|
|
200
|
+
console.log();
|
|
201
|
+
}
|
|
202
|
+
async function listWorkspacesCmd() {
|
|
203
|
+
const workspaces = await listWorkspaces();
|
|
204
|
+
const currentWorkspace = getCurrentWorkspace();
|
|
205
|
+
if (workspaces.length === 0) {
|
|
206
|
+
console.log(c.gray("No workspaces found."));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
console.log(c.bold(`
|
|
210
|
+
Workspaces (${workspaces.length})
|
|
211
|
+
`));
|
|
212
|
+
const workspacesWithCurrent = workspaces.map((ws) => ({
|
|
213
|
+
...ws,
|
|
214
|
+
current: ws.name === currentWorkspace ? c.green("\u25CF") : "",
|
|
215
|
+
progress: `${ws.completedCount}/${ws.taskCount}`
|
|
216
|
+
}));
|
|
217
|
+
const columns = [
|
|
218
|
+
{ header: "", key: "current", width: 2 },
|
|
219
|
+
{ header: "Name", key: "name", width: COLUMN_WIDTH_PROJECT_NAME, format: (v) => c.cyan(String(v)) },
|
|
220
|
+
{ header: "Tasks", key: "progress", width: COLUMN_WIDTH_PRIORITY }
|
|
221
|
+
];
|
|
222
|
+
console.log(table(workspacesWithCurrent, columns));
|
|
223
|
+
console.log();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/interactive.ts
|
|
227
|
+
import * as readline from "readline";
|
|
228
|
+
async function prompt(question) {
|
|
229
|
+
const rl = readline.createInterface({
|
|
230
|
+
input: process.stdin,
|
|
231
|
+
output: process.stdout
|
|
232
|
+
});
|
|
233
|
+
return new Promise((resolve) => {
|
|
234
|
+
rl.question(question, (answer) => {
|
|
235
|
+
rl.close();
|
|
236
|
+
resolve(answer.trim());
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async function showMenu(title, options) {
|
|
241
|
+
console.clear();
|
|
242
|
+
console.log(banner("TASK MCP"));
|
|
243
|
+
console.log();
|
|
244
|
+
console.log(c.bold(c.cyan(title)));
|
|
245
|
+
console.log(c.dim("\u2500".repeat(MENU_SEPARATOR_WIDTH)));
|
|
246
|
+
console.log();
|
|
247
|
+
for (const opt of options) {
|
|
248
|
+
const desc = opt.description ? c.dim(` - ${opt.description}`) : "";
|
|
249
|
+
console.log(` ${c.yellow(opt.key)} ${opt.label}${desc}`);
|
|
250
|
+
}
|
|
251
|
+
console.log();
|
|
252
|
+
const choice = await prompt(c.cyan("Select option: "));
|
|
253
|
+
const selected = options.find((o) => o.key.toLowerCase() === choice.toLowerCase());
|
|
254
|
+
if (selected) {
|
|
255
|
+
await selected.action();
|
|
256
|
+
} else if (choice !== "") {
|
|
257
|
+
console.log(c.error(`Invalid option: ${choice}`));
|
|
258
|
+
await sleep(ERROR_MESSAGE_DELAY_MS);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function sleep(ms) {
|
|
262
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
263
|
+
}
|
|
264
|
+
async function waitForKey(message = "Press Enter to continue...") {
|
|
265
|
+
await prompt(c.dim(message));
|
|
266
|
+
}
|
|
267
|
+
async function mainMenu() {
|
|
268
|
+
const options = [
|
|
269
|
+
{
|
|
270
|
+
key: "d",
|
|
271
|
+
label: "Dashboard",
|
|
272
|
+
description: "View workspace dashboard",
|
|
273
|
+
action: async () => {
|
|
274
|
+
await dashboardMenu();
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
key: "w",
|
|
279
|
+
label: "Workspaces",
|
|
280
|
+
description: "List workspaces",
|
|
281
|
+
action: async () => {
|
|
282
|
+
await workspacesMenu();
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
key: "t",
|
|
287
|
+
label: "Tasks",
|
|
288
|
+
description: "List and filter tasks",
|
|
289
|
+
action: async () => {
|
|
290
|
+
await tasksMenu();
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
key: "x",
|
|
295
|
+
label: "Exit",
|
|
296
|
+
description: "Exit interactive mode",
|
|
297
|
+
action: () => {
|
|
298
|
+
console.log(c.dim("Goodbye!"));
|
|
299
|
+
process.exit(0);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
];
|
|
303
|
+
await showMenu("Main Menu", options);
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
async function dashboardMenu() {
|
|
307
|
+
console.clear();
|
|
308
|
+
console.log(c.bold(c.cyan("Dashboard")));
|
|
309
|
+
console.log(c.dim("\u2500".repeat(MENU_SEPARATOR_WIDTH)));
|
|
310
|
+
console.log();
|
|
311
|
+
const workspace = await prompt(c.cyan("Workspace name (Enter for current): "));
|
|
312
|
+
console.log();
|
|
313
|
+
await dashboard(workspace || undefined);
|
|
314
|
+
console.log();
|
|
315
|
+
await waitForKey();
|
|
316
|
+
}
|
|
317
|
+
async function workspacesMenu() {
|
|
318
|
+
const options = [
|
|
319
|
+
{
|
|
320
|
+
key: "l",
|
|
321
|
+
label: "List Workspaces",
|
|
322
|
+
action: async () => {
|
|
323
|
+
console.clear();
|
|
324
|
+
await listWorkspacesCmd();
|
|
325
|
+
console.log();
|
|
326
|
+
await waitForKey();
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
key: "b",
|
|
331
|
+
label: "Back to Main Menu",
|
|
332
|
+
action: () => {}
|
|
333
|
+
}
|
|
334
|
+
];
|
|
335
|
+
await showMenu("Workspaces", options);
|
|
336
|
+
}
|
|
337
|
+
async function tasksMenu() {
|
|
338
|
+
const options = [
|
|
339
|
+
{
|
|
340
|
+
key: "l",
|
|
341
|
+
label: "List All Tasks",
|
|
342
|
+
action: async () => {
|
|
343
|
+
console.clear();
|
|
344
|
+
await listTasksCmd({ all: false });
|
|
345
|
+
console.log();
|
|
346
|
+
await waitForKey();
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
key: "p",
|
|
351
|
+
label: "Filter by Priority",
|
|
352
|
+
action: async () => {
|
|
353
|
+
console.clear();
|
|
354
|
+
const priority = await prompt(c.cyan("Priority (critical/high/medium/low): "));
|
|
355
|
+
if (priority) {
|
|
356
|
+
await listTasksCmd({ priority });
|
|
357
|
+
}
|
|
358
|
+
console.log();
|
|
359
|
+
await waitForKey();
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
key: "s",
|
|
364
|
+
label: "Filter by Status",
|
|
365
|
+
action: async () => {
|
|
366
|
+
console.clear();
|
|
367
|
+
const status = await prompt(c.cyan("Status (pending/in_progress/blocked/completed): "));
|
|
368
|
+
if (status) {
|
|
369
|
+
await listTasksCmd({ status });
|
|
370
|
+
}
|
|
371
|
+
console.log();
|
|
372
|
+
await waitForKey();
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
key: "a",
|
|
377
|
+
label: "All (include completed)",
|
|
378
|
+
action: async () => {
|
|
379
|
+
console.clear();
|
|
380
|
+
await listTasksCmd({ all: true });
|
|
381
|
+
console.log();
|
|
382
|
+
await waitForKey();
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
key: "b",
|
|
387
|
+
label: "Back to Main Menu",
|
|
388
|
+
action: () => {}
|
|
389
|
+
}
|
|
390
|
+
];
|
|
391
|
+
await showMenu("Tasks", options);
|
|
392
|
+
}
|
|
393
|
+
async function startInteractive() {
|
|
394
|
+
console.clear();
|
|
395
|
+
console.log(banner("TASK MCP"));
|
|
396
|
+
console.log();
|
|
397
|
+
console.log(c.bold("Welcome to Task MCP Interactive Mode"));
|
|
398
|
+
console.log(c.dim("Navigate using keyboard shortcuts"));
|
|
399
|
+
console.log();
|
|
400
|
+
await sleep(WELCOME_SCREEN_DELAY_MS);
|
|
401
|
+
while (true) {
|
|
402
|
+
try {
|
|
403
|
+
await mainMenu();
|
|
404
|
+
} catch (error) {
|
|
405
|
+
if (error instanceof Error && error.message.includes("readline was closed")) {
|
|
406
|
+
console.log();
|
|
407
|
+
console.log(c.dim("Goodbye!"));
|
|
408
|
+
process.exit(0);
|
|
409
|
+
}
|
|
410
|
+
throw error;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/commands/inbox.ts
|
|
416
|
+
import { parseInboxInput } from "@task-mcp/shared";
|
|
417
|
+
async function inboxAddCmd(options) {
|
|
418
|
+
const parsed = parseInboxInput(options.content);
|
|
419
|
+
const inboxStore = getInboxStore();
|
|
420
|
+
const item = await inboxStore.add({
|
|
421
|
+
content: parsed.content,
|
|
422
|
+
source: options.source ?? "cli",
|
|
423
|
+
tags: parsed.tags
|
|
424
|
+
});
|
|
425
|
+
console.log();
|
|
426
|
+
console.log(c.green("\u2713") + " Captured to inbox");
|
|
427
|
+
console.log();
|
|
428
|
+
console.log(" " + c.dim("ID:") + " " + c.cyan(item.id));
|
|
429
|
+
console.log(" " + c.dim("Content:") + " " + item.content);
|
|
430
|
+
if (parsed.tags?.length) {
|
|
431
|
+
console.log(" " + c.dim("Tags:") + " " + parsed.tags.map((t) => c.yellow("#" + t)).join(" "));
|
|
432
|
+
}
|
|
433
|
+
console.log();
|
|
434
|
+
console.log(c.dim("Use 'task inbox promote " + item.id + "' to convert to task"));
|
|
435
|
+
console.log();
|
|
436
|
+
}
|
|
437
|
+
async function inboxListCmd(options) {
|
|
438
|
+
const status = options.all ? undefined : options.status ?? "pending";
|
|
439
|
+
const inboxStore = getInboxStore();
|
|
440
|
+
const items = await inboxStore.list({ status });
|
|
441
|
+
console.log();
|
|
442
|
+
if (items.length === 0) {
|
|
443
|
+
console.log(c.dim(status ? `No ${status} items in inbox.` : "Inbox is empty."));
|
|
444
|
+
console.log();
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const title = status ? `Inbox (${status})` : "Inbox (all)";
|
|
448
|
+
console.log(c.bold(c.cyan(title)) + c.dim(` - ${items.length} items`));
|
|
449
|
+
console.log(c.dim("\u2500".repeat(LIST_SEPARATOR_WIDTH)));
|
|
450
|
+
console.log();
|
|
451
|
+
for (const item of items) {
|
|
452
|
+
const statusIcon = item.status === "pending" ? c.yellow("\u25CB") : item.status === "promoted" ? c.green("\u2713") : c.dim("\xD7");
|
|
453
|
+
const date = new Date(item.capturedAt).toLocaleDateString();
|
|
454
|
+
const tags = item.tags?.length ? " " + item.tags.map((t) => c.dim("#" + t)).join(" ") : "";
|
|
455
|
+
console.log(`${statusIcon} ${c.cyan(item.id)} ${c.dim("(" + date + ")")}`);
|
|
456
|
+
console.log(` ${item.content}${tags}`);
|
|
457
|
+
if (item.promotedToTaskId) {
|
|
458
|
+
console.log(` ${c.dim("\u2192 Task:")} ${c.green(item.promotedToTaskId)}`);
|
|
459
|
+
}
|
|
460
|
+
console.log();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async function inboxGetCmd(itemId) {
|
|
464
|
+
const inboxStore = getInboxStore();
|
|
465
|
+
const item = await inboxStore.get(itemId);
|
|
466
|
+
if (!item) {
|
|
467
|
+
console.log();
|
|
468
|
+
console.log(c.error(`Inbox item not found: ${itemId}`));
|
|
469
|
+
console.log();
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
console.log();
|
|
473
|
+
console.log(c.bold(c.cyan("Inbox Item")));
|
|
474
|
+
console.log(c.dim("\u2500".repeat(MENU_SEPARATOR_WIDTH)));
|
|
475
|
+
console.log();
|
|
476
|
+
console.log(" " + c.dim("ID:") + " " + item.id);
|
|
477
|
+
console.log(" " + c.dim("Status:") + " " + formatStatus(item.status));
|
|
478
|
+
console.log(" " + c.dim("Captured:") + " " + new Date(item.capturedAt).toLocaleString());
|
|
479
|
+
if (item.source) {
|
|
480
|
+
console.log(" " + c.dim("Source:") + " " + item.source);
|
|
481
|
+
}
|
|
482
|
+
if (item.tags?.length) {
|
|
483
|
+
console.log(" " + c.dim("Tags:") + " " + item.tags.map((t) => c.yellow("#" + t)).join(" "));
|
|
484
|
+
}
|
|
485
|
+
console.log();
|
|
486
|
+
console.log(c.bold("Content:"));
|
|
487
|
+
console.log(" " + item.content);
|
|
488
|
+
if (item.promotedToTaskId) {
|
|
489
|
+
console.log();
|
|
490
|
+
console.log(c.green("\u2192 Promoted to: " + item.promotedToTaskId));
|
|
491
|
+
}
|
|
492
|
+
console.log();
|
|
493
|
+
}
|
|
494
|
+
async function inboxPromoteCmd(options) {
|
|
495
|
+
const inboxStore = getInboxStore();
|
|
496
|
+
const taskStore = getTaskStore();
|
|
497
|
+
const item = await inboxStore.get(options.itemId);
|
|
498
|
+
if (!item) {
|
|
499
|
+
console.log();
|
|
500
|
+
console.log(c.error(`Inbox item not found: ${options.itemId}`));
|
|
501
|
+
console.log();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (item.status === "promoted") {
|
|
505
|
+
console.log();
|
|
506
|
+
console.log(c.warning(`Item already promoted to: ${item.promotedToTaskId}`));
|
|
507
|
+
console.log();
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const task = await taskStore.create({
|
|
511
|
+
title: options.title ?? item.content.slice(0, INBOX_PROMOTE_TITLE_MAX_LENGTH),
|
|
512
|
+
description: item.content,
|
|
513
|
+
priority: options.priority ?? "medium",
|
|
514
|
+
tags: item.tags
|
|
515
|
+
});
|
|
516
|
+
await inboxStore.promote(options.itemId, task.id);
|
|
517
|
+
console.log();
|
|
518
|
+
console.log(c.green("\u2713") + " Promoted to task");
|
|
519
|
+
console.log();
|
|
520
|
+
console.log(" " + c.dim("Task ID:") + " " + c.cyan(task.id));
|
|
521
|
+
console.log(" " + c.dim("Title:") + " " + task.title);
|
|
522
|
+
console.log(" " + c.dim("Workspace:") + " " + task.workspace);
|
|
523
|
+
console.log(" " + c.dim("Priority:") + " " + formatPriority(task.priority));
|
|
524
|
+
console.log();
|
|
525
|
+
}
|
|
526
|
+
async function inboxDiscardCmd(itemId) {
|
|
527
|
+
const inboxStore = getInboxStore();
|
|
528
|
+
const item = await inboxStore.discard(itemId);
|
|
529
|
+
if (!item) {
|
|
530
|
+
console.log();
|
|
531
|
+
console.log(c.error(`Inbox item not found: ${itemId}`));
|
|
532
|
+
console.log();
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
console.log();
|
|
536
|
+
console.log(c.dim("\xD7") + " Discarded: " + item.content.slice(0, INBOX_DISCARD_PREVIEW_LENGTH) + "...");
|
|
537
|
+
console.log();
|
|
538
|
+
}
|
|
539
|
+
async function inboxDeleteCmd(itemId) {
|
|
540
|
+
const inboxStore = getInboxStore();
|
|
541
|
+
const deleted = await inboxStore.delete(itemId);
|
|
542
|
+
if (!deleted) {
|
|
543
|
+
console.log();
|
|
544
|
+
console.log(c.error(`Inbox item not found: ${itemId}`));
|
|
545
|
+
console.log();
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
console.log();
|
|
549
|
+
console.log(c.green("\u2713") + " Deleted: " + itemId);
|
|
550
|
+
console.log();
|
|
551
|
+
}
|
|
552
|
+
async function inboxCountCmd() {
|
|
553
|
+
const inboxStore = getInboxStore();
|
|
554
|
+
const count = await inboxStore.getPendingCount();
|
|
555
|
+
console.log();
|
|
556
|
+
if (count > 0) {
|
|
557
|
+
console.log(c.yellow("\uD83D\uDCE5") + ` ${count} pending item${count === 1 ? "" : "s"} in inbox`);
|
|
558
|
+
} else {
|
|
559
|
+
console.log(c.dim("Inbox is empty"));
|
|
560
|
+
}
|
|
561
|
+
console.log();
|
|
562
|
+
}
|
|
563
|
+
// package.json
|
|
564
|
+
var package_default = {
|
|
565
|
+
name: "@task-mcp/cli",
|
|
566
|
+
version: "1.0.21",
|
|
567
|
+
description: "Zero-dependency CLI for task-mcp with Bun native visualization",
|
|
568
|
+
type: "module",
|
|
569
|
+
bin: {
|
|
570
|
+
task: "./dist/index.js"
|
|
571
|
+
},
|
|
572
|
+
main: "./dist/index.js",
|
|
573
|
+
files: [
|
|
574
|
+
"dist",
|
|
575
|
+
"README.md"
|
|
576
|
+
],
|
|
577
|
+
scripts: {
|
|
578
|
+
build: "bun build ./src/index.ts --outdir ./dist --target bun --external @task-mcp/shared --external @task-mcp/mcp-server",
|
|
579
|
+
dev: "bun run ./src/index.ts",
|
|
580
|
+
dashboard: "bun run ./src/index.ts dashboard",
|
|
581
|
+
typecheck: "bun --bun tsc --noEmit"
|
|
582
|
+
},
|
|
583
|
+
keywords: [
|
|
584
|
+
"task-mcp",
|
|
585
|
+
"cli",
|
|
586
|
+
"task-management",
|
|
587
|
+
"terminal",
|
|
588
|
+
"dashboard"
|
|
589
|
+
],
|
|
590
|
+
author: "addsalt1t",
|
|
591
|
+
license: "MIT",
|
|
592
|
+
repository: {
|
|
593
|
+
type: "git",
|
|
594
|
+
url: "git+https://github.com/addsalt1t/task-mcp.git",
|
|
595
|
+
directory: "packages/cli"
|
|
596
|
+
},
|
|
597
|
+
dependencies: {
|
|
598
|
+
"@task-mcp/mcp-server": "workspace:*",
|
|
599
|
+
"@task-mcp/shared": "workspace:*"
|
|
600
|
+
},
|
|
601
|
+
devDependencies: {
|
|
602
|
+
"@types/bun": "^1.1.14",
|
|
603
|
+
typescript: "^5.7.2"
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// src/index.ts
|
|
608
|
+
var VERSION = package_default.version;
|
|
609
|
+
function parseArgs(argv) {
|
|
610
|
+
const args = argv.slice(2);
|
|
611
|
+
const flags = {};
|
|
612
|
+
const positional = [];
|
|
613
|
+
for (let i = 0;i < args.length; i++) {
|
|
614
|
+
const arg = args[i];
|
|
615
|
+
if (arg.startsWith("--")) {
|
|
616
|
+
const [key, value] = arg.slice(2).split("=");
|
|
617
|
+
if (value !== undefined) {
|
|
618
|
+
flags[key] = value;
|
|
619
|
+
} else if (args[i + 1] && !args[i + 1].startsWith("-")) {
|
|
620
|
+
flags[key] = args[++i];
|
|
621
|
+
} else {
|
|
622
|
+
flags[key] = true;
|
|
623
|
+
}
|
|
624
|
+
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
625
|
+
const key = arg.slice(1);
|
|
626
|
+
if (args[i + 1] && !args[i + 1].startsWith("-")) {
|
|
627
|
+
flags[key] = args[++i];
|
|
628
|
+
} else {
|
|
629
|
+
flags[key] = true;
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
positional.push(arg);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
command: positional[0] ?? "dashboard",
|
|
637
|
+
args: positional.slice(1),
|
|
638
|
+
flags
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function showHelp() {
|
|
642
|
+
console.log(`
|
|
643
|
+
${c.cyan(c.bold("Task MCP CLI"))} ${c.gray(`v${VERSION}`)}
|
|
644
|
+
${c.dim("Zero-dependency terminal visualization for task-mcp")}
|
|
645
|
+
|
|
646
|
+
${c.yellow("USAGE")}
|
|
647
|
+
${c.bold("task")} [command] [options]
|
|
648
|
+
|
|
649
|
+
${c.yellow("COMMANDS")}
|
|
650
|
+
${c.cyan("dashboard")} [workspace] Show workspace dashboard (default)
|
|
651
|
+
${c.cyan("d")} [workspace] Alias for dashboard
|
|
652
|
+
${c.cyan("list")} List all tasks
|
|
653
|
+
${c.cyan("ls")} Alias for list
|
|
654
|
+
${c.cyan("workspaces")} List all workspaces
|
|
655
|
+
${c.cyan("ws")} Alias for workspaces
|
|
656
|
+
${c.cyan("inbox")} <subcommand> Manage inbox (add/list/get/promote/discard)
|
|
657
|
+
${c.cyan("interactive")} Start interactive mode
|
|
658
|
+
${c.cyan("i")} Alias for interactive
|
|
659
|
+
${c.cyan("help")} Show this help message
|
|
660
|
+
${c.cyan("version")} Show version
|
|
661
|
+
|
|
662
|
+
${c.yellow("INBOX SUBCOMMANDS")}
|
|
663
|
+
${c.cyan("inbox")} "content" Quick capture to inbox
|
|
664
|
+
${c.cyan("inbox add")} "content" Add item with natural language
|
|
665
|
+
${c.cyan("inbox list")} List pending items
|
|
666
|
+
${c.cyan("inbox get")} <id> Show item details
|
|
667
|
+
${c.cyan("inbox promote")} <id> Promote to task
|
|
668
|
+
${c.cyan("inbox discard")} <id> Discard item
|
|
669
|
+
${c.cyan("inbox count")} Show pending count
|
|
670
|
+
|
|
671
|
+
${c.yellow("OPTIONS")}
|
|
672
|
+
${c.cyan("-i, --interactive")} Start interactive mode
|
|
673
|
+
${c.cyan("--status")} <status> Filter tasks by status (pending,in_progress,blocked,completed,cancelled)
|
|
674
|
+
${c.cyan("--priority")} <priority> Filter tasks by priority (critical,high,medium,low)
|
|
675
|
+
${c.cyan("--all")} Include completed/cancelled tasks
|
|
676
|
+
|
|
677
|
+
${c.yellow("EXAMPLES")}
|
|
678
|
+
${c.dim("# Show dashboard for all projects")}
|
|
679
|
+
task
|
|
680
|
+
|
|
681
|
+
${c.dim("# Show dashboard for a specific project")}
|
|
682
|
+
task dashboard proj_abc123
|
|
683
|
+
|
|
684
|
+
${c.dim("# List pending high-priority tasks")}
|
|
685
|
+
task list --status pending --priority high
|
|
686
|
+
|
|
687
|
+
${c.dim("# List all workspaces")}
|
|
688
|
+
task workspaces --all
|
|
689
|
+
`);
|
|
690
|
+
}
|
|
691
|
+
function showVersion() {
|
|
692
|
+
console.log(`task-mcp-cli v${VERSION}`);
|
|
693
|
+
}
|
|
694
|
+
async function handleInboxCommand(args, flags) {
|
|
695
|
+
const subcommand = args[0];
|
|
696
|
+
const subcommands = ["add", "list", "ls", "get", "promote", "discard", "delete", "count"];
|
|
697
|
+
if (subcommand && !subcommands.includes(subcommand)) {
|
|
698
|
+
await inboxAddCmd({ content: args.join(" ") });
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
switch (subcommand) {
|
|
702
|
+
case "add":
|
|
703
|
+
if (!args[1]) {
|
|
704
|
+
console.log(c.error('Usage: task inbox add "content"'));
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
await inboxAddCmd({ content: args.slice(1).join(" ") });
|
|
708
|
+
break;
|
|
709
|
+
case "list":
|
|
710
|
+
case "ls":
|
|
711
|
+
await inboxListCmd({
|
|
712
|
+
all: flags.all === true,
|
|
713
|
+
status: flags.status
|
|
714
|
+
});
|
|
715
|
+
break;
|
|
716
|
+
case "get":
|
|
717
|
+
if (!args[1]) {
|
|
718
|
+
console.log(c.error("Usage: task inbox get <item-id>"));
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
await inboxGetCmd(args[1]);
|
|
722
|
+
break;
|
|
723
|
+
case "promote":
|
|
724
|
+
if (!args[1]) {
|
|
725
|
+
console.log(c.error("Usage: task inbox promote <item-id> [--priority <p>]"));
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
await inboxPromoteCmd({
|
|
729
|
+
itemId: args[1],
|
|
730
|
+
title: flags.title,
|
|
731
|
+
priority: flags.priority
|
|
732
|
+
});
|
|
733
|
+
break;
|
|
734
|
+
case "discard":
|
|
735
|
+
if (!args[1]) {
|
|
736
|
+
console.log(c.error("Usage: task inbox discard <item-id>"));
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
await inboxDiscardCmd(args[1]);
|
|
740
|
+
break;
|
|
741
|
+
case "delete":
|
|
742
|
+
if (!args[1]) {
|
|
743
|
+
console.log(c.error("Usage: task inbox delete <item-id>"));
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
await inboxDeleteCmd(args[1]);
|
|
747
|
+
break;
|
|
748
|
+
case "count":
|
|
749
|
+
await inboxCountCmd();
|
|
750
|
+
break;
|
|
751
|
+
default:
|
|
752
|
+
await inboxListCmd({ all: false });
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
async function main() {
|
|
757
|
+
const { command, args, flags } = parseArgs(process.argv);
|
|
758
|
+
if (flags.i === true || flags.interactive === true) {
|
|
759
|
+
await startInteractive();
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
try {
|
|
763
|
+
switch (command) {
|
|
764
|
+
case "dashboard":
|
|
765
|
+
case "d":
|
|
766
|
+
await dashboard(args[0]);
|
|
767
|
+
break;
|
|
768
|
+
case "list":
|
|
769
|
+
case "ls":
|
|
770
|
+
await listTasksCmd({
|
|
771
|
+
workspace: args[0],
|
|
772
|
+
status: flags.status,
|
|
773
|
+
priority: flags.priority,
|
|
774
|
+
all: flags.all === true
|
|
775
|
+
});
|
|
776
|
+
break;
|
|
777
|
+
case "workspaces":
|
|
778
|
+
case "ws":
|
|
779
|
+
await listWorkspacesCmd();
|
|
780
|
+
break;
|
|
781
|
+
case "interactive":
|
|
782
|
+
case "i":
|
|
783
|
+
await startInteractive();
|
|
784
|
+
break;
|
|
785
|
+
case "inbox":
|
|
786
|
+
await handleInboxCommand(args, flags);
|
|
787
|
+
break;
|
|
788
|
+
case "help":
|
|
789
|
+
case "-h":
|
|
790
|
+
case "--help":
|
|
791
|
+
showHelp();
|
|
792
|
+
break;
|
|
793
|
+
case "version":
|
|
794
|
+
case "-v":
|
|
795
|
+
case "--version":
|
|
796
|
+
showVersion();
|
|
797
|
+
break;
|
|
798
|
+
default:
|
|
799
|
+
console.log(c.error(`Unknown command: ${command}`));
|
|
800
|
+
console.log(c.dim("Run 'task help' for usage information."));
|
|
801
|
+
process.exit(1);
|
|
802
|
+
}
|
|
803
|
+
} catch (error) {
|
|
804
|
+
if (error instanceof Error) {
|
|
805
|
+
console.error(c.error(`Error: ${error.message}`));
|
|
806
|
+
} else {
|
|
807
|
+
console.error(c.error("An unexpected error occurred"));
|
|
808
|
+
}
|
|
809
|
+
process.exit(1);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
if (import.meta.main) {
|
|
813
|
+
main();
|
|
814
|
+
}
|
|
815
|
+
export {
|
|
816
|
+
parseArgs,
|
|
817
|
+
VERSION
|
|
818
|
+
};
|