@zoobbe/cli 1.2.3 → 1.3.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zoobbe/cli",
3
- "version": "1.2.3",
3
+ "version": "1.3.0",
4
4
  "description": "Zoobbe CLI - Manage boards, cards, and projects from the terminal",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -23,30 +23,21 @@ card
23
23
  let cards = [];
24
24
 
25
25
  if (options.board) {
26
- // Get lists with populated cards for this board
26
+ // Get lists with populated cards for this board (single API call)
27
27
  const listsData = await withSpinner('Fetching cards...', () =>
28
28
  client.get(`/boards/${options.board}/lists`)
29
29
  );
30
30
  const lists = listsData.lists || listsData.data || listsData;
31
31
  const listArr = Array.isArray(lists) ? lists : Object.values(lists);
32
32
 
33
- // Collect card shortIds per list
34
- const cardRefs = [];
33
+ // Extract cards directly from list data (no extra API calls)
35
34
  for (const list of listArr) {
36
35
  if (list.cards && Array.isArray(list.cards)) {
37
36
  for (const c of list.cards) {
38
- cardRefs.push({ id: c.shortId || c._id, listName: list.title || '' });
37
+ cards.push({ ...c, _listName: list.title || '' });
39
38
  }
40
39
  }
41
40
  }
42
-
43
- // Fetch full card details in parallel
44
- const results = await Promise.allSettled(
45
- cardRefs.map(ref => client.get(`/cards/${ref.id}`).then(d => ({ ...d, _listName: ref.listName })))
46
- );
47
- for (const r of results) {
48
- if (r.status === 'fulfilled') cards.push(r.value);
49
- }
50
41
  } else {
51
42
  // Get cards for current user
52
43
  const userId = config.get('userId');
@@ -333,7 +324,7 @@ card
333
324
  .action(async (cardId) => {
334
325
  try {
335
326
  await withSpinner('Updating card...', () =>
336
- client.post(`/cards/update/${cardId}`, { isComplete: true })
327
+ client.post(`/cards/update/${cardId}`, { completed: true })
337
328
  );
338
329
 
339
330
  success(`Card ${cardId} marked as complete`);
@@ -658,7 +649,7 @@ card
658
649
  .action(async (cardId) => {
659
650
  try {
660
651
  await withSpinner('Updating card...', () =>
661
- client.post(`/cards/update/${cardId}`, { isComplete: false })
652
+ client.post(`/cards/update/${cardId}`, { completed: false })
662
653
  );
663
654
  success(`Card ${cardId} marked as incomplete`);
664
655
  } catch (err) {
package/src/mcp-server.js CHANGED
@@ -19,6 +19,25 @@ const config = require('./lib/config');
19
19
  // Mark all requests from MCP server as AI agent actions
20
20
  client.setSource('mcp');
21
21
 
22
+ // ── Priority Normalization ──
23
+ // Backend expects: Low, Normal, High, Urgent
24
+ // MCP accepts both formats for convenience
25
+ const PRIORITY_MAP = {
26
+ low: 'Low',
27
+ normal: 'Normal',
28
+ medium: 'Normal',
29
+ high: 'High',
30
+ urgent: 'Urgent',
31
+ critical: 'Urgent',
32
+ };
33
+
34
+ function normalizePriority(value) {
35
+ if (!value) return null;
36
+ const mapped = PRIORITY_MAP[value.toLowerCase()];
37
+ if (!mapped) throw new Error(`Invalid priority "${value}". Valid: Low, Normal, High, Urgent (or: low, medium, high, critical)`);
38
+ return mapped;
39
+ }
40
+
22
41
  // ── Tool Definitions ──
23
42
 
24
43
  const TOOLS = [
@@ -201,7 +220,7 @@ const TOOLS = [
201
220
  list_id: { type: 'string', description: 'List ID (uses first list if omitted)' },
202
221
  description: { type: 'string', description: 'Card description' },
203
222
  due_date: { type: 'string', description: 'Due date (YYYY-MM-DD)' },
204
- priority: { type: 'string', description: 'Priority: low, medium, high, critical' },
223
+ priority: { type: 'string', description: 'Priority: Low, Normal, High, Urgent (also accepts: low, medium, high, critical)' },
205
224
  },
206
225
  required: ['title', 'board_id'],
207
226
  },
@@ -297,7 +316,7 @@ const TOOLS = [
297
316
  type: 'object',
298
317
  properties: {
299
318
  card_id: { type: 'string', description: 'Card short ID' },
300
- priority: { type: 'string', description: 'Priority: low, medium, high, critical' },
319
+ priority: { type: 'string', description: 'Priority: Low, Normal, High, Urgent (also accepts: low, medium, high, critical)' },
301
320
  },
302
321
  required: ['card_id', 'priority'],
303
322
  },
@@ -383,7 +402,7 @@ async function executeTool(name, args = {}) {
383
402
  }
384
403
 
385
404
  case 'list_lists': {
386
- const data = await client.get(`/${args.board_id}/actionLists/`);
405
+ const data = await client.get(`/boards/${args.board_id}/lists`);
387
406
  let lists = data.lists || data.data || data;
388
407
  lists = Array.isArray(lists) ? lists : Object.values(lists);
389
408
  return lists.filter(l => !l.archived).map(l => ({
@@ -427,21 +446,16 @@ async function executeTool(name, args = {}) {
427
446
  const lists = listsData.lists || listsData.data || listsData;
428
447
  const listArr = Array.isArray(lists) ? lists : Object.values(lists);
429
448
 
430
- const cardRefs = [];
449
+ // Extract cards directly from list data (single API call, no N+1)
450
+ let cards = [];
431
451
  for (const list of listArr) {
432
452
  if (list.cards && Array.isArray(list.cards)) {
433
453
  for (const c of list.cards) {
434
- cardRefs.push({ id: c.shortId || c._id, listName: list.title || '' });
454
+ cards.push({ ...c, _listName: list.title || '' });
435
455
  }
436
456
  }
437
457
  }
438
458
 
439
- const results = await Promise.allSettled(
440
- cardRefs.map(ref => client.get(`/cards/${ref.id}`).then(d => ({ ...d, _listName: ref.listName })))
441
- );
442
-
443
- let cards = results.filter(r => r.status === 'fulfilled').map(r => r.value);
444
-
445
459
  if (args.due) {
446
460
  const now = new Date();
447
461
  cards = cards.filter(c => {
@@ -457,6 +471,7 @@ async function executeTool(name, args = {}) {
457
471
  return cards.map(c => ({
458
472
  title: c.title || c.name,
459
473
  id: c.shortId || c._id,
474
+ cardNumber: c.cardNumber || null,
460
475
  list: c._listName || c.actionListTitle || '',
461
476
  due: c.dueDate?.date || c.dueDate || null,
462
477
  members: c.members?.length || 0,
@@ -471,6 +486,7 @@ async function executeTool(name, args = {}) {
471
486
  return {
472
487
  title: c.title || c.name,
473
488
  id: c.shortId || c._id,
489
+ cardNumber: c.cardNumber || null,
474
490
  board: c.boardTitle || c.board,
475
491
  list: c.actionListTitle || c.actionList?.name,
476
492
  description: c.description || '',
@@ -525,6 +541,7 @@ async function executeTool(name, args = {}) {
525
541
  return (Array.isArray(cards) ? cards : []).map(c => ({
526
542
  title: c.title || c.name,
527
543
  id: c.shortId || c._id,
544
+ cardNumber: c.cardNumber || null,
528
545
  board: c.boardTitle || '',
529
546
  list: c.actionListTitle || '',
530
547
  due: c.dueDate?.date || c.dueDate || null,
@@ -534,8 +551,13 @@ async function executeTool(name, args = {}) {
534
551
  }
535
552
 
536
553
  case 'search': {
537
- const data = await client.get(`/search?q=${encodeURIComponent(args.query)}&workspaceId=${encodeURIComponent(workspaceId)}`);
538
- return data.data || data;
554
+ const data = await client.post(`/global/search?query=${encodeURIComponent(args.query)}`, {});
555
+ const results = data.data || data;
556
+ const out = [];
557
+ if (results.boards) out.push(...results.boards.map(b => ({ type: 'board', title: b.title || b.name, id: b.shortId || b._id })));
558
+ if (results.cards) out.push(...results.cards.map(c => ({ type: 'card', title: c.title, id: c.shortId || c._id, board: c.boardTitle || '' })));
559
+ if (results.pages) out.push(...results.pages.map(p => ({ type: 'page', title: p.title, id: p.shortId || p._id })));
560
+ return out.length ? out : results;
539
561
  }
540
562
 
541
563
  case 'list_notifications': {
@@ -580,10 +602,10 @@ async function executeTool(name, args = {}) {
580
602
  await client.post(`/cards/${cardId}/duedate`, { dueDate: args.due_date });
581
603
  }
582
604
  if (args.priority) {
583
- await client.post(`/cards/${cardId}/priority`, { priority: args.priority });
605
+ await client.post(`/cards/${cardId}/priority`, { priority: normalizePriority(args.priority) });
584
606
  }
585
607
 
586
- return { created: true, title: args.title, id: cardId, changed_by: 'AI Agent (via MCP)' };
608
+ return { created: true, title: args.title, id: cardId, cardNumber: c.cardNumber || null, changed_by: 'AI Agent (via MCP)' };
587
609
  }
588
610
 
589
611
  case 'update_card': {
@@ -637,12 +659,12 @@ async function executeTool(name, args = {}) {
637
659
  }
638
660
 
639
661
  case 'mark_card_done': {
640
- await client.post(`/cards/update/${args.card_id}`, { isComplete: true });
662
+ await client.post(`/cards/update/${args.card_id}`, { completed: true });
641
663
  return { done: true, card_id: args.card_id, changed_by: 'AI Agent (via MCP)' };
642
664
  }
643
665
 
644
666
  case 'mark_card_undone': {
645
- await client.post(`/cards/update/${args.card_id}`, { isComplete: false });
667
+ await client.post(`/cards/update/${args.card_id}`, { completed: false });
646
668
  return { undone: true, card_id: args.card_id, changed_by: 'AI Agent (via MCP)' };
647
669
  }
648
670
 
@@ -656,8 +678,9 @@ async function executeTool(name, args = {}) {
656
678
  }
657
679
 
658
680
  case 'set_priority': {
659
- await client.post(`/cards/${args.card_id}/priority`, { priority: args.priority });
660
- return { set: true, card_id: args.card_id, priority: args.priority, changed_by: 'AI Agent (via MCP)' };
681
+ const normalized = normalizePriority(args.priority);
682
+ await client.post(`/cards/${args.card_id}/priority`, { priority: normalized });
683
+ return { set: true, card_id: args.card_id, priority: normalized, changed_by: 'AI Agent (via MCP)' };
661
684
  }
662
685
 
663
686
  case 'archive_card': {
@@ -701,7 +724,7 @@ async function executeTool(name, args = {}) {
701
724
 
702
725
  const SERVER_INFO = {
703
726
  name: 'zoobbe',
704
- version: '1.2.2',
727
+ version: '1.3.0',
705
728
  };
706
729
 
707
730
  function makeResponse(id, result) {