@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/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@task-mcp/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "Zero-dependency CLI for task-mcp with Bun native visualization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"task": "./
|
|
7
|
+
"task": "./dist/index.js"
|
|
8
8
|
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
9
10
|
"files": [
|
|
10
|
-
"
|
|
11
|
+
"dist",
|
|
11
12
|
"README.md"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|
|
15
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun --external @task-mcp/shared --external @task-mcp/mcp-server",
|
|
14
16
|
"dev": "bun run ./src/index.ts",
|
|
15
17
|
"dashboard": "bun run ./src/index.ts dashboard",
|
|
16
18
|
"typecheck": "bun --bun tsc --noEmit"
|
|
@@ -30,8 +32,8 @@
|
|
|
30
32
|
"directory": "packages/cli"
|
|
31
33
|
},
|
|
32
34
|
"dependencies": {
|
|
33
|
-
"@task-mcp/mcp-server": "
|
|
34
|
-
"@task-mcp/shared": "
|
|
35
|
+
"@task-mcp/mcp-server": "1.0.11",
|
|
36
|
+
"@task-mcp/shared": "1.0.10"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@types/bun": "^1.1.14",
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
color,
|
|
4
|
-
style,
|
|
5
|
-
styled,
|
|
6
|
-
stripAnsi,
|
|
7
|
-
displayWidth,
|
|
8
|
-
pad,
|
|
9
|
-
truncate,
|
|
10
|
-
progressBar,
|
|
11
|
-
hline,
|
|
12
|
-
c,
|
|
13
|
-
} from "../ansi.js";
|
|
14
|
-
|
|
15
|
-
describe("ansi", () => {
|
|
16
|
-
describe("color", () => {
|
|
17
|
-
test("applies color code to text", () => {
|
|
18
|
-
const result = color("hello", "red");
|
|
19
|
-
expect(result).toContain("hello");
|
|
20
|
-
expect(result).toContain("\x1b[31m"); // red color code
|
|
21
|
-
expect(result).toContain("\x1b[0m"); // reset code
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("applies different colors", () => {
|
|
25
|
-
expect(color("test", "green")).toContain("\x1b[32m");
|
|
26
|
-
expect(color("test", "blue")).toContain("\x1b[34m");
|
|
27
|
-
expect(color("test", "cyan")).toContain("\x1b[36m");
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("style", () => {
|
|
32
|
-
test("applies bold style", () => {
|
|
33
|
-
const result = style("hello", "bold");
|
|
34
|
-
expect(result).toContain("hello");
|
|
35
|
-
expect(result).toContain("\x1b[1m"); // bold code
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("applies dim style", () => {
|
|
39
|
-
const result = style("hello", "dim");
|
|
40
|
-
expect(result).toContain("\x1b[2m"); // dim code
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe("styled", () => {
|
|
45
|
-
test("combines color and style", () => {
|
|
46
|
-
const result = styled("hello", "red", "bold");
|
|
47
|
-
expect(result).toContain("hello");
|
|
48
|
-
expect(result).toContain("1;31m"); // bold + red
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("works without style", () => {
|
|
52
|
-
const result = styled("hello", "green");
|
|
53
|
-
expect(result).toContain("\x1b[32m");
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe("c convenience functions", () => {
|
|
58
|
-
test("c.red applies red color", () => {
|
|
59
|
-
expect(c.red("test")).toContain("\x1b[31m");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("c.green applies green color", () => {
|
|
63
|
-
expect(c.green("test")).toContain("\x1b[32m");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("c.bold applies bold style", () => {
|
|
67
|
-
expect(c.bold("test")).toContain("\x1b[1m");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("semantic colors work", () => {
|
|
71
|
-
expect(c.success("ok")).toContain("\x1b[32m"); // green
|
|
72
|
-
expect(c.error("fail")).toContain("\x1b[31m"); // red
|
|
73
|
-
expect(c.warning("warn")).toContain("\x1b[33m"); // yellow
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe("stripAnsi", () => {
|
|
78
|
-
test("removes ANSI codes from string", () => {
|
|
79
|
-
const colored = "\x1b[31mhello\x1b[0m";
|
|
80
|
-
expect(stripAnsi(colored)).toBe("hello");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("handles multiple ANSI codes", () => {
|
|
84
|
-
const styled = "\x1b[1;31mhello\x1b[0m \x1b[32mworld\x1b[0m";
|
|
85
|
-
expect(stripAnsi(styled)).toBe("hello world");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("returns original string if no ANSI codes", () => {
|
|
89
|
-
expect(stripAnsi("hello world")).toBe("hello world");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("handles empty string", () => {
|
|
93
|
-
expect(stripAnsi("")).toBe("");
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe("displayWidth", () => {
|
|
98
|
-
test("returns correct width for ASCII", () => {
|
|
99
|
-
expect(displayWidth("hello")).toBe(5);
|
|
100
|
-
expect(displayWidth("test")).toBe(4);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test("returns correct width for Korean (2 per char)", () => {
|
|
104
|
-
expect(displayWidth("안녕")).toBe(4); // 2 chars * 2 width
|
|
105
|
-
expect(displayWidth("테스트")).toBe(6); // 3 chars * 2 width
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("handles mixed ASCII and Korean", () => {
|
|
109
|
-
expect(displayWidth("hello안녕")).toBe(9); // 5 + 4
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("strips ANSI codes before calculating", () => {
|
|
113
|
-
const colored = c.red("hello");
|
|
114
|
-
expect(displayWidth(colored)).toBe(5);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("handles empty string", () => {
|
|
118
|
-
expect(displayWidth("")).toBe(0);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe("pad", () => {
|
|
123
|
-
test("pads string to width (left align)", () => {
|
|
124
|
-
expect(pad("hi", 5)).toBe("hi ");
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("pads string to width (right align)", () => {
|
|
128
|
-
expect(pad("hi", 5, "right")).toBe(" hi");
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test("pads string to width (center align)", () => {
|
|
132
|
-
expect(pad("hi", 6, "center")).toBe(" hi ");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("returns original string if already at width", () => {
|
|
136
|
-
expect(pad("hello", 5)).toBe("hello");
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("returns original string if longer than width", () => {
|
|
140
|
-
expect(pad("hello world", 5)).toBe("hello world");
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("handles ANSI codes correctly", () => {
|
|
144
|
-
const colored = c.red("hi");
|
|
145
|
-
const padded = pad(colored, 5);
|
|
146
|
-
expect(stripAnsi(padded)).toBe("hi ");
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe("truncate", () => {
|
|
151
|
-
test("truncates long string with suffix", () => {
|
|
152
|
-
expect(truncate("hello world", 8)).toBe("hello...");
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("returns original string if within limit", () => {
|
|
156
|
-
expect(truncate("hello", 10)).toBe("hello");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("uses custom suffix", () => {
|
|
160
|
-
expect(truncate("hello world", 8, "~")).toBe("hello w~");
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test("handles Korean characters", () => {
|
|
164
|
-
const result = truncate("안녕하세요", 6);
|
|
165
|
-
expect(displayWidth(result)).toBeLessThanOrEqual(6);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test("handles empty string", () => {
|
|
169
|
-
expect(truncate("", 5)).toBe("");
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
describe("progressBar", () => {
|
|
174
|
-
test("creates progress bar at 0%", () => {
|
|
175
|
-
const bar = progressBar(0, 10);
|
|
176
|
-
expect(bar).toContain("0%");
|
|
177
|
-
expect(stripAnsi(bar)).toContain("░░░░░░░░░░░░░░░░░░░░");
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
test("creates progress bar at 50%", () => {
|
|
181
|
-
const bar = progressBar(5, 10);
|
|
182
|
-
expect(bar).toContain("50%");
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
test("creates progress bar at 100%", () => {
|
|
186
|
-
const bar = progressBar(10, 10);
|
|
187
|
-
expect(bar).toContain("100%");
|
|
188
|
-
expect(stripAnsi(bar)).toContain("████████████████████");
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
test("handles custom width", () => {
|
|
192
|
-
const bar = progressBar(5, 10, { width: 10, showPercent: false });
|
|
193
|
-
const stripped = stripAnsi(bar);
|
|
194
|
-
expect(stripped.length).toBe(10);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("handles zero total", () => {
|
|
198
|
-
const bar = progressBar(0, 0);
|
|
199
|
-
expect(bar).toContain("0%");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("hides percent when showPercent is false", () => {
|
|
203
|
-
const bar = progressBar(5, 10, { showPercent: false });
|
|
204
|
-
expect(bar).not.toContain("%");
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
describe("hline", () => {
|
|
209
|
-
test("creates horizontal line of specified width", () => {
|
|
210
|
-
expect(hline(5)).toBe("─────");
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test("uses custom character", () => {
|
|
214
|
-
expect(hline(3, "=")).toBe("===");
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test("handles zero width", () => {
|
|
218
|
-
expect(hline(0)).toBe("");
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
});
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import type { Task, InboxItem, WorkspaceInfo } from "../storage.js";
|
|
3
|
-
|
|
4
|
-
// Helper to create test tasks
|
|
5
|
-
function createTask(overrides: Partial<Task> = {}): Task {
|
|
6
|
-
return {
|
|
7
|
-
id: `task_${Math.random().toString(36).slice(2, 10)}`,
|
|
8
|
-
title: "Test Task",
|
|
9
|
-
status: "pending",
|
|
10
|
-
priority: "medium",
|
|
11
|
-
workspace: "test-workspace",
|
|
12
|
-
createdAt: new Date().toISOString(),
|
|
13
|
-
updatedAt: new Date().toISOString(),
|
|
14
|
-
...overrides,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Helper to create test workspaces
|
|
19
|
-
function createWorkspace(overrides: Partial<WorkspaceInfo> = {}): WorkspaceInfo {
|
|
20
|
-
return {
|
|
21
|
-
name: "Test Workspace",
|
|
22
|
-
taskCount: 0,
|
|
23
|
-
completedCount: 0,
|
|
24
|
-
...overrides,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Helper to create inbox items
|
|
29
|
-
function createInboxItem(overrides: Partial<InboxItem> = {}): InboxItem {
|
|
30
|
-
return {
|
|
31
|
-
id: `inbox_${Math.random().toString(36).slice(2, 10)}`,
|
|
32
|
-
content: "Test inbox item",
|
|
33
|
-
status: "pending",
|
|
34
|
-
capturedAt: new Date().toISOString(),
|
|
35
|
-
source: "cli",
|
|
36
|
-
...overrides,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
describe("dashboard command - unit tests", () => {
|
|
41
|
-
describe("workspace lookup logic", () => {
|
|
42
|
-
test("finds workspace by exact name", () => {
|
|
43
|
-
const workspaces = [
|
|
44
|
-
createWorkspace({ name: "workspace-a" }),
|
|
45
|
-
createWorkspace({ name: "workspace-b" }),
|
|
46
|
-
];
|
|
47
|
-
|
|
48
|
-
const workspaceName = "workspace-a";
|
|
49
|
-
const workspace = workspaces.find((w) => w.name === workspaceName || w.name.startsWith(workspaceName));
|
|
50
|
-
|
|
51
|
-
expect(workspace).toBeDefined();
|
|
52
|
-
expect(workspace?.name).toBe("workspace-a");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("finds workspace by partial name prefix", () => {
|
|
56
|
-
const workspaces = [
|
|
57
|
-
createWorkspace({ name: "workspace-abc123" }),
|
|
58
|
-
createWorkspace({ name: "workspace-xyz789" }),
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
const workspaceName = "workspace-abc";
|
|
62
|
-
const workspace = workspaces.find((w) => w.name === workspaceName || w.name.startsWith(workspaceName));
|
|
63
|
-
|
|
64
|
-
expect(workspace).toBeDefined();
|
|
65
|
-
expect(workspace?.name).toBe("workspace-abc123");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("returns undefined for non-matching name", () => {
|
|
69
|
-
const workspaces = [
|
|
70
|
-
createWorkspace({ name: "workspace-a" }),
|
|
71
|
-
createWorkspace({ name: "workspace-b" }),
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
const workspaceName = "workspace-nonexistent";
|
|
75
|
-
const workspace = workspaces.find((w) => w.name === workspaceName || w.name.startsWith(workspaceName));
|
|
76
|
-
|
|
77
|
-
expect(workspace).toBeUndefined();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("returns first match when multiple workspaces share prefix", () => {
|
|
81
|
-
const workspaces = [
|
|
82
|
-
createWorkspace({ name: "workspace-abc111" }),
|
|
83
|
-
createWorkspace({ name: "workspace-abc222" }),
|
|
84
|
-
];
|
|
85
|
-
|
|
86
|
-
const workspaceName = "workspace-abc";
|
|
87
|
-
const workspace = workspaces.find((w) => w.name === workspaceName || w.name.startsWith(workspaceName));
|
|
88
|
-
|
|
89
|
-
expect(workspace).toBeDefined();
|
|
90
|
-
expect(workspace?.name).toBe("workspace-abc111");
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe("dashboard mode selection", () => {
|
|
95
|
-
test("single workspace mode when only one workspace exists", () => {
|
|
96
|
-
const workspaces = [createWorkspace({ name: "only-workspace" })];
|
|
97
|
-
|
|
98
|
-
const hasSingleWorkspace = workspaces.length === 1;
|
|
99
|
-
const shouldShowGlobal = workspaces.length > 1;
|
|
100
|
-
|
|
101
|
-
expect(hasSingleWorkspace).toBe(true);
|
|
102
|
-
expect(shouldShowGlobal).toBe(false);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("global mode when multiple workspaces exist", () => {
|
|
106
|
-
const workspaces = [
|
|
107
|
-
createWorkspace({ name: "workspace-1" }),
|
|
108
|
-
createWorkspace({ name: "workspace-2" }),
|
|
109
|
-
];
|
|
110
|
-
|
|
111
|
-
const hasSingleWorkspace = workspaces.length === 1;
|
|
112
|
-
const shouldShowGlobal = workspaces.length > 1;
|
|
113
|
-
|
|
114
|
-
expect(hasSingleWorkspace).toBe(false);
|
|
115
|
-
expect(shouldShowGlobal).toBe(true);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test("workspace mode when specific workspace provided", () => {
|
|
119
|
-
const workspaces = [
|
|
120
|
-
createWorkspace({ name: "workspace-1" }),
|
|
121
|
-
createWorkspace({ name: "workspace-2" }),
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
const workspaceName = "workspace-1";
|
|
125
|
-
const targetWorkspace = workspaces.find((w) => w.name === workspaceName || w.name.startsWith(workspaceName));
|
|
126
|
-
|
|
127
|
-
expect(targetWorkspace).toBeDefined();
|
|
128
|
-
expect(targetWorkspace?.name).toBe("workspace-1");
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe("task grouping by workspace", () => {
|
|
133
|
-
test("builds task map for each workspace", () => {
|
|
134
|
-
const workspaces = [
|
|
135
|
-
createWorkspace({ name: "workspace-1" }),
|
|
136
|
-
createWorkspace({ name: "workspace-2" }),
|
|
137
|
-
];
|
|
138
|
-
|
|
139
|
-
const allTasks = [
|
|
140
|
-
createTask({ id: "task_1", workspace: "workspace-1" }),
|
|
141
|
-
createTask({ id: "task_2", workspace: "workspace-1" }),
|
|
142
|
-
createTask({ id: "task_3", workspace: "workspace-2" }),
|
|
143
|
-
];
|
|
144
|
-
|
|
145
|
-
const tasksByWorkspace = new Map<string, Task[]>();
|
|
146
|
-
for (const ws of workspaces) {
|
|
147
|
-
tasksByWorkspace.set(
|
|
148
|
-
ws.name,
|
|
149
|
-
allTasks.filter((t) => t.workspace === ws.name)
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
expect(tasksByWorkspace.get("workspace-1")?.length).toBe(2);
|
|
154
|
-
expect(tasksByWorkspace.get("workspace-2")?.length).toBe(1);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("returns empty array for workspace with no tasks", () => {
|
|
158
|
-
const tasksByWorkspace = new Map<string, Task[]>();
|
|
159
|
-
tasksByWorkspace.set("workspace-1", []);
|
|
160
|
-
|
|
161
|
-
const getWorkspaceTasks = (name: string) => tasksByWorkspace.get(name) ?? [];
|
|
162
|
-
|
|
163
|
-
expect(getWorkspaceTasks("workspace-1")).toEqual([]);
|
|
164
|
-
expect(getWorkspaceTasks("workspace-nonexistent")).toEqual([]);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe("inbox filtering", () => {
|
|
169
|
-
test("filters inbox items by pending status", () => {
|
|
170
|
-
const items = [
|
|
171
|
-
createInboxItem({ id: "inbox_1", status: "pending" }),
|
|
172
|
-
createInboxItem({ id: "inbox_2", status: "promoted" }),
|
|
173
|
-
createInboxItem({ id: "inbox_3", status: "pending" }),
|
|
174
|
-
createInboxItem({ id: "inbox_4", status: "discarded" }),
|
|
175
|
-
];
|
|
176
|
-
|
|
177
|
-
const pendingItems = items.filter((i) => i.status === "pending");
|
|
178
|
-
|
|
179
|
-
expect(pendingItems.length).toBe(2);
|
|
180
|
-
expect(pendingItems.map((i) => i.id).sort()).toEqual(["inbox_1", "inbox_3"]);
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
describe("empty state handling", () => {
|
|
185
|
-
test("detects when no workspaces exist", () => {
|
|
186
|
-
const workspaces: WorkspaceInfo[] = [];
|
|
187
|
-
|
|
188
|
-
const hasWorkspaces = workspaces.length > 0;
|
|
189
|
-
|
|
190
|
-
expect(hasWorkspaces).toBe(false);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test("detects when tasks are empty", () => {
|
|
194
|
-
const tasks: Task[] = [];
|
|
195
|
-
|
|
196
|
-
const hasTasks = tasks.length > 0;
|
|
197
|
-
|
|
198
|
-
expect(hasTasks).toBe(false);
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
describe("dashboard command - exports", () => {
|
|
204
|
-
test("dashboard function is exported", async () => {
|
|
205
|
-
const { dashboard } = await import("../commands/dashboard.js");
|
|
206
|
-
expect(typeof dashboard).toBe("function");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("dashboard accepts optional projectId parameter", async () => {
|
|
210
|
-
const { dashboard } = await import("../commands/dashboard.js");
|
|
211
|
-
|
|
212
|
-
// Verify function signature allows optional string parameter
|
|
213
|
-
// This is a type-level check, doesn't execute the function
|
|
214
|
-
const fn: (projectId?: string) => Promise<void> = dashboard;
|
|
215
|
-
expect(typeof fn).toBe("function");
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
describe("dashboard rendering options", () => {
|
|
220
|
-
test("version option structure", () => {
|
|
221
|
-
const options = { version: "1.0.0" };
|
|
222
|
-
|
|
223
|
-
expect(options.version).toBe("1.0.0");
|
|
224
|
-
expect(typeof options.version).toBe("string");
|
|
225
|
-
});
|
|
226
|
-
});
|