@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/src/storage.ts
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Storage utilities
|
|
3
|
-
* Wrapper functions for store access and pure functions for task statistics
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Task, InboxItem } from "@task-mcp/shared";
|
|
7
|
-
import type { WorkspaceInfo } from "@task-mcp/shared";
|
|
8
|
-
import {
|
|
9
|
-
TaskStore,
|
|
10
|
-
InboxStore,
|
|
11
|
-
StateStore,
|
|
12
|
-
} from "@task-mcp/mcp-server/storage";
|
|
13
|
-
|
|
14
|
-
// Re-export types from shared for consumers of this module
|
|
15
|
-
export type { Task, InboxItem, WorkspaceInfo };
|
|
16
|
-
|
|
17
|
-
// Initialize stores
|
|
18
|
-
const taskStore = new TaskStore();
|
|
19
|
-
const inboxStore = new InboxStore();
|
|
20
|
-
const stateStore = new StateStore();
|
|
21
|
-
|
|
22
|
-
// =============================================================================
|
|
23
|
-
// Store Wrapper Functions
|
|
24
|
-
// =============================================================================
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get current workspace name
|
|
28
|
-
*/
|
|
29
|
-
export function getCurrentWorkspace(): string {
|
|
30
|
-
return taskStore.currentWorkspace;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* List all workspaces
|
|
35
|
-
*/
|
|
36
|
-
export async function listWorkspaces(): Promise<WorkspaceInfo[]> {
|
|
37
|
-
const workspaceNames = await taskStore.listWorkspaces();
|
|
38
|
-
const workspaces: WorkspaceInfo[] = [];
|
|
39
|
-
|
|
40
|
-
for (const name of workspaceNames) {
|
|
41
|
-
const tasks = await taskStore.list({ workspaces: [name] });
|
|
42
|
-
workspaces.push({
|
|
43
|
-
name,
|
|
44
|
-
taskCount: tasks.length,
|
|
45
|
-
completedCount: tasks.filter(t => t.status === "completed").length,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return workspaces;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* List tasks for current workspace
|
|
54
|
-
*/
|
|
55
|
-
export async function listTasks(): Promise<Task[]> {
|
|
56
|
-
return taskStore.list();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* List all tasks across all workspaces
|
|
61
|
-
*/
|
|
62
|
-
export async function listAllTasks(): Promise<Task[]> {
|
|
63
|
-
return taskStore.listAll();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* List inbox items by status
|
|
68
|
-
*/
|
|
69
|
-
export async function listInboxItems(
|
|
70
|
-
status?: "pending" | "promoted" | "discarded"
|
|
71
|
-
): Promise<InboxItem[]> {
|
|
72
|
-
return inboxStore.list({ status });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Get the active tag (git branch context)
|
|
77
|
-
*/
|
|
78
|
-
export async function getActiveTag(): Promise<string> {
|
|
79
|
-
return stateStore.getActiveTag();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Get workspace statistics
|
|
84
|
-
*/
|
|
85
|
-
export interface WorkspaceStats {
|
|
86
|
-
total: number;
|
|
87
|
-
completed: number;
|
|
88
|
-
inProgress: number;
|
|
89
|
-
pending: number;
|
|
90
|
-
blocked: number;
|
|
91
|
-
cancelled: number;
|
|
92
|
-
byPriority: Record<string, number>;
|
|
93
|
-
completionPercent: number;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function calculateStats(tasks: Task[]): WorkspaceStats {
|
|
97
|
-
const stats: WorkspaceStats = {
|
|
98
|
-
total: tasks.length,
|
|
99
|
-
completed: 0,
|
|
100
|
-
inProgress: 0,
|
|
101
|
-
pending: 0,
|
|
102
|
-
blocked: 0,
|
|
103
|
-
cancelled: 0,
|
|
104
|
-
byPriority: { critical: 0, high: 0, medium: 0, low: 0 },
|
|
105
|
-
completionPercent: 0,
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
for (const task of tasks) {
|
|
109
|
-
switch (task.status) {
|
|
110
|
-
case "completed":
|
|
111
|
-
stats.completed++;
|
|
112
|
-
break;
|
|
113
|
-
case "in_progress":
|
|
114
|
-
stats.inProgress++;
|
|
115
|
-
break;
|
|
116
|
-
case "pending":
|
|
117
|
-
stats.pending++;
|
|
118
|
-
break;
|
|
119
|
-
case "blocked":
|
|
120
|
-
stats.blocked++;
|
|
121
|
-
break;
|
|
122
|
-
case "cancelled":
|
|
123
|
-
stats.cancelled++;
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
stats.byPriority[task.priority]++;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const nonCancelled = stats.total - stats.cancelled;
|
|
130
|
-
stats.completionPercent = nonCancelled > 0
|
|
131
|
-
? Math.round((stats.completed / nonCancelled) * 100)
|
|
132
|
-
: 0;
|
|
133
|
-
|
|
134
|
-
return stats;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Get dependency metrics
|
|
139
|
-
*/
|
|
140
|
-
export interface DependencyMetrics {
|
|
141
|
-
noDependencies: number;
|
|
142
|
-
readyToWork: number;
|
|
143
|
-
blockedByDependencies: number;
|
|
144
|
-
mostDependedOn: { id: string; title: string; count: number } | null;
|
|
145
|
-
avgDependencies: number;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export function calculateDependencyMetrics(tasks: Task[]): DependencyMetrics {
|
|
149
|
-
const activeTasks = tasks.filter(t => t.status !== "completed" && t.status !== "cancelled");
|
|
150
|
-
const completedIds = new Set(tasks.filter(t => t.status === "completed").map(t => t.id));
|
|
151
|
-
|
|
152
|
-
// Count how many tasks depend on each task
|
|
153
|
-
const dependentCounts: Record<string, number> = {};
|
|
154
|
-
for (const task of activeTasks) {
|
|
155
|
-
for (const dep of task.dependencies ?? []) {
|
|
156
|
-
dependentCounts[dep.taskId] = (dependentCounts[dep.taskId] ?? 0) + 1;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let mostDependedOn: DependencyMetrics["mostDependedOn"] = null;
|
|
161
|
-
let maxCount = 0;
|
|
162
|
-
for (const [id, count] of Object.entries(dependentCounts)) {
|
|
163
|
-
if (count > maxCount) {
|
|
164
|
-
maxCount = count;
|
|
165
|
-
const task = tasks.find(t => t.id === id);
|
|
166
|
-
if (task) {
|
|
167
|
-
mostDependedOn = { id, title: task.title, count };
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const noDependencies = activeTasks.filter(t => (t.dependencies?.length ?? 0) === 0).length;
|
|
173
|
-
|
|
174
|
-
// Ready to work: no uncompleted dependencies
|
|
175
|
-
const readyToWork = activeTasks.filter(t => {
|
|
176
|
-
const deps = t.dependencies ?? [];
|
|
177
|
-
if (deps.length === 0) return true;
|
|
178
|
-
return deps.every(d => completedIds.has(d.taskId));
|
|
179
|
-
}).length;
|
|
180
|
-
|
|
181
|
-
const blockedByDependencies = activeTasks.length - readyToWork;
|
|
182
|
-
|
|
183
|
-
const totalDeps = activeTasks.reduce((sum, t) => sum + (t.dependencies?.length ?? 0), 0);
|
|
184
|
-
const avgDependencies = activeTasks.length > 0 ? totalDeps / activeTasks.length : 0;
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
noDependencies,
|
|
188
|
-
readyToWork,
|
|
189
|
-
blockedByDependencies,
|
|
190
|
-
mostDependedOn,
|
|
191
|
-
avgDependencies: Math.round(avgDependencies * 10) / 10,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Get next task suggestion
|
|
197
|
-
*/
|
|
198
|
-
export function suggestNextTask(tasks: Task[]): Task | null {
|
|
199
|
-
const completedIds = new Set(tasks.filter(t => t.status === "completed").map(t => t.id));
|
|
200
|
-
|
|
201
|
-
// Filter to actionable tasks
|
|
202
|
-
const actionable = tasks.filter(t => {
|
|
203
|
-
if (t.status !== "pending" && t.status !== "in_progress") return false;
|
|
204
|
-
|
|
205
|
-
// Check if all dependencies are completed
|
|
206
|
-
const deps = t.dependencies ?? [];
|
|
207
|
-
return deps.every(d => completedIds.has(d.taskId));
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
if (actionable.length === 0) return null;
|
|
211
|
-
|
|
212
|
-
// Sort by priority then by creation date
|
|
213
|
-
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
214
|
-
actionable.sort((a, b) => {
|
|
215
|
-
const pDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
216
|
-
if (pDiff !== 0) return pDiff;
|
|
217
|
-
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
return actionable[0] ?? null;
|
|
221
|
-
}
|