@zoobbe/cli 1.2.0 → 1.2.2
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 +243 -19
- package/bin/zoobbe-mcp +5 -0
- package/package.json +6 -3
- package/src/commands/activity.js +102 -0
- package/src/commands/analytics.js +281 -0
- package/src/commands/api-key.js +140 -0
- package/src/commands/auth.js +2 -0
- package/src/commands/automation.js +255 -0
- package/src/commands/board.js +295 -0
- package/src/commands/card.js +367 -0
- package/src/commands/checklist.js +173 -0
- package/src/commands/import.js +101 -0
- package/src/commands/list.js +213 -0
- package/src/commands/notification.js +92 -0
- package/src/commands/page.js +253 -0
- package/src/commands/timer.js +234 -0
- package/src/commands/webhook.js +141 -0
- package/src/commands/workspace.js +174 -0
- package/src/index.js +10 -0
- package/src/lib/client.js +42 -2
- package/src/mcp-server.js +797 -0
- package/src/utils/format.js +39 -0
- package/src/utils/prompts.js +40 -0
- package/src/utils/resolve.js +67 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function formatDate(isoString) {
|
|
2
|
+
if (!isoString) return '-';
|
|
3
|
+
return new Date(isoString).toLocaleDateString();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function formatDuration(ms) {
|
|
7
|
+
if (!ms || ms <= 0) return '0s';
|
|
8
|
+
const seconds = Math.floor(ms / 1000);
|
|
9
|
+
const h = Math.floor(seconds / 3600);
|
|
10
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
11
|
+
const s = seconds % 60;
|
|
12
|
+
const parts = [];
|
|
13
|
+
if (h > 0) parts.push(`${h}h`);
|
|
14
|
+
if (m > 0) parts.push(`${m}m`);
|
|
15
|
+
if (s > 0 || parts.length === 0) parts.push(`${s}s`);
|
|
16
|
+
return parts.join(' ');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function truncate(str, max = 50) {
|
|
20
|
+
if (!str) return '';
|
|
21
|
+
return str.length > max ? str.substring(0, max - 1) + '…' : str;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function formatRelativeTime(date) {
|
|
25
|
+
if (!date) return '-';
|
|
26
|
+
const now = new Date();
|
|
27
|
+
const d = new Date(date);
|
|
28
|
+
const diffMs = now - d;
|
|
29
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
30
|
+
if (diffMins < 1) return 'just now';
|
|
31
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
32
|
+
const diffHours = Math.floor(diffMins / 60);
|
|
33
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
34
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
35
|
+
if (diffDays < 30) return `${diffDays}d ago`;
|
|
36
|
+
return formatDate(date);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { formatDate, formatDuration, truncate, formatRelativeTime };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
|
|
3
|
+
async function selectFromList(items, { displayField = 'name', valueField = '_id', message = 'Select:' } = {}) {
|
|
4
|
+
const choices = items.map((item, i) => ({
|
|
5
|
+
name: `${i + 1}. ${typeof displayField === 'function' ? displayField(item) : item[displayField] || item}`,
|
|
6
|
+
value: typeof valueField === 'function' ? valueField(item) : item[valueField] || item,
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const { selected } = await inquirer.prompt([{
|
|
10
|
+
type: 'list',
|
|
11
|
+
name: 'selected',
|
|
12
|
+
message,
|
|
13
|
+
choices,
|
|
14
|
+
}]);
|
|
15
|
+
|
|
16
|
+
return selected;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function confirmAction(message = 'Are you sure?') {
|
|
20
|
+
const { confirmed } = await inquirer.prompt([{
|
|
21
|
+
type: 'confirm',
|
|
22
|
+
name: 'confirmed',
|
|
23
|
+
message,
|
|
24
|
+
default: false,
|
|
25
|
+
}]);
|
|
26
|
+
return confirmed;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function promptInput(message, { validate, defaultValue } = {}) {
|
|
30
|
+
const { value } = await inquirer.prompt([{
|
|
31
|
+
type: 'input',
|
|
32
|
+
name: 'value',
|
|
33
|
+
message,
|
|
34
|
+
default: defaultValue,
|
|
35
|
+
validate: validate || (() => true),
|
|
36
|
+
}]);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { selectFromList, confirmAction, promptInput };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const client = require('../lib/client');
|
|
2
|
+
const config = require('../lib/config');
|
|
3
|
+
|
|
4
|
+
async function resolveWorkspace(nameOrId) {
|
|
5
|
+
const data = await client.get('/workspaces/me');
|
|
6
|
+
const workspaces = data.data || data;
|
|
7
|
+
const match = workspaces.find(ws =>
|
|
8
|
+
ws.shortId === nameOrId ||
|
|
9
|
+
ws.name.toLowerCase() === nameOrId.toLowerCase() ||
|
|
10
|
+
ws.shortName?.toLowerCase() === nameOrId.toLowerCase()
|
|
11
|
+
);
|
|
12
|
+
return match || null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function resolveBoard(nameOrId, workspaceId) {
|
|
16
|
+
const wsId = workspaceId || config.get('activeWorkspace');
|
|
17
|
+
const data = await client.get(`/boards?workspaceId=${encodeURIComponent(wsId)}`);
|
|
18
|
+
const boards = data.data || data;
|
|
19
|
+
const match = boards.find(b =>
|
|
20
|
+
b.shortId === nameOrId ||
|
|
21
|
+
(b.title || b.name || '').toLowerCase() === nameOrId.toLowerCase()
|
|
22
|
+
);
|
|
23
|
+
return match || null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function resolveList(nameOrId, boardId) {
|
|
27
|
+
const data = await client.get(`/boards/${boardId}/lists`);
|
|
28
|
+
const lists = data.lists || data.data || data;
|
|
29
|
+
const listArr = Array.isArray(lists) ? lists : Object.values(lists);
|
|
30
|
+
const match = listArr.find(l =>
|
|
31
|
+
l._id === nameOrId ||
|
|
32
|
+
l._id?.toString() === nameOrId ||
|
|
33
|
+
(l.title || '').toLowerCase() === nameOrId.toLowerCase()
|
|
34
|
+
);
|
|
35
|
+
return match || null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function resolveMember(nameOrEmail, workspaceId) {
|
|
39
|
+
const wsId = workspaceId || config.get('activeWorkspace');
|
|
40
|
+
const data = await client.get(`/members/workspace/${wsId}`);
|
|
41
|
+
const members = data.data || data;
|
|
42
|
+
const match = members.find(m => {
|
|
43
|
+
const user = m.user || m;
|
|
44
|
+
return user.email === nameOrEmail ||
|
|
45
|
+
user.userName === nameOrEmail ||
|
|
46
|
+
user.username === nameOrEmail ||
|
|
47
|
+
user._id === nameOrEmail;
|
|
48
|
+
});
|
|
49
|
+
return match || null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function requireWorkspace() {
|
|
53
|
+
const workspaceId = config.get('activeWorkspace');
|
|
54
|
+
if (!workspaceId) {
|
|
55
|
+
throw new Error('No active workspace. Run "zoobbe workspace switch <name>".');
|
|
56
|
+
}
|
|
57
|
+
return workspaceId;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function resolveWorkspaceObjectId(shortId) {
|
|
61
|
+
const data = await client.get('/workspaces/me');
|
|
62
|
+
const workspaces = data.data || data;
|
|
63
|
+
const ws = workspaces.find(w => w.shortId === shortId);
|
|
64
|
+
return ws ? ws._id : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { resolveWorkspace, resolveBoard, resolveList, resolveMember, requireWorkspace, resolveWorkspaceObjectId };
|