slyplan-mcp 1.7.3 → 1.7.5

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.
Files changed (3) hide show
  1. package/dist/cli.js +4 -5
  2. package/dist/db.js +41 -28
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -347,11 +347,10 @@ function mergeMcpJson(existing, auth) {
347
347
  else if (auth.refreshToken) {
348
348
  env.SLYPLAN_REFRESH_TOKEN = auth.refreshToken;
349
349
  }
350
- result.mcpServers.slyplan = {
351
- command: 'npx',
352
- args: ['-y', 'slyplan-mcp@latest'],
353
- env,
354
- };
350
+ const isWindows = process.platform === 'win32';
351
+ result.mcpServers.slyplan = isWindows
352
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', 'slyplan-mcp@latest'], env }
353
+ : { command: 'npx', args: ['-y', 'slyplan-mcp@latest'], env };
355
354
  return result;
356
355
  }
357
356
  // Hooks go in .claude/settings.json
package/dist/db.js CHANGED
@@ -1,5 +1,25 @@
1
1
  import { supabase, getUserId } from './supabase.js';
2
2
  import { v4 as uuid } from 'uuid';
3
+ // --- Helpers ---
4
+ // Explicit column lists to reduce Supabase egress (avoid select('*'))
5
+ const NODE_COLUMNS = 'id,parent_id,type,title,description,status,progress,sort_order,collapsed,metadata,created_by,created_at,updated_at';
6
+ const LINK_COLUMNS = 'id,node_id,url,label,link_type,created_at';
7
+ /** Batch-fetch links for multiple node IDs in a single query */
8
+ async function batchGetLinks(nodeIds) {
9
+ const linksByNode = new Map();
10
+ if (nodeIds.length === 0)
11
+ return linksByNode;
12
+ const { data: linkRows } = await supabase
13
+ .from('links')
14
+ .select(LINK_COLUMNS)
15
+ .in('node_id', nodeIds);
16
+ for (const link of (linkRows || [])) {
17
+ const arr = linksByNode.get(link.node_id) || [];
18
+ arr.push(dbLinkToLink(link));
19
+ linksByNode.set(link.node_id, arr);
20
+ }
21
+ return linksByNode;
22
+ }
3
23
  function dbNodeToTree(row, links, children) {
4
24
  return {
5
25
  id: row.id,
@@ -32,17 +52,17 @@ function dbLinkToLink(row) {
32
52
  export async function getTree(rootId, maxDepth) {
33
53
  const { data: allNodes, error: nodesErr } = await supabase
34
54
  .from('nodes')
35
- .select('*')
55
+ .select(NODE_COLUMNS)
36
56
  .order('sort_order', { ascending: true });
37
57
  if (nodesErr)
38
58
  throw new Error(nodesErr.message);
39
59
  const { data: allLinks, error: linksErr } = await supabase
40
60
  .from('links')
41
- .select('*');
61
+ .select(LINK_COLUMNS);
42
62
  if (linksErr)
43
63
  throw new Error(linksErr.message);
44
64
  const linksByNode = new Map();
45
- for (const link of allLinks || []) {
65
+ for (const link of (allLinks || [])) {
46
66
  const arr = linksByNode.get(link.node_id) || [];
47
67
  arr.push(dbLinkToLink(link));
48
68
  linksByNode.set(link.node_id, arr);
@@ -88,16 +108,13 @@ export async function getTree(rootId, maxDepth) {
88
108
  export async function getNode(id, maxDepth = 1) {
89
109
  const { data: row, error } = await supabase
90
110
  .from('nodes')
91
- .select('*')
111
+ .select(NODE_COLUMNS)
92
112
  .eq('id', id)
93
113
  .single();
94
114
  if (error || !row)
95
115
  return null;
96
- const { data: linkRows } = await supabase
97
- .from('links')
98
- .select('*')
99
- .eq('node_id', id);
100
- const links = (linkRows || []).map(dbLinkToLink);
116
+ const linkMap = await batchGetLinks([id]);
117
+ const links = linkMap.get(id) || [];
101
118
  const children = maxDepth > 0 ? await getChildrenRecursive(id, maxDepth, 1) : [];
102
119
  let progress = row.progress;
103
120
  if (children.length > 0) {
@@ -108,15 +125,17 @@ export async function getNode(id, maxDepth = 1) {
108
125
  async function getChildrenRecursive(parentId, maxDepth, currentDepth) {
109
126
  const { data: childRows } = await supabase
110
127
  .from('nodes')
111
- .select('*')
128
+ .select(NODE_COLUMNS)
112
129
  .eq('parent_id', parentId)
113
130
  .order('sort_order', { ascending: true });
131
+ const typedChildren = (childRows || []);
132
+ if (typedChildren.length === 0)
133
+ return [];
134
+ // Batch-fetch all links for this level in ONE query instead of N+1
135
+ const childIds = typedChildren.map(c => c.id);
136
+ const linkMap = await batchGetLinks(childIds);
114
137
  const children = [];
115
- for (const child of (childRows || [])) {
116
- const { data: childLinkRows } = await supabase
117
- .from('links')
118
- .select('*')
119
- .eq('node_id', child.id);
138
+ for (const child of typedChildren) {
120
139
  const grandchildren = currentDepth < maxDepth
121
140
  ? await getChildrenRecursive(child.id, maxDepth, currentDepth + 1)
122
141
  : [];
@@ -124,7 +143,7 @@ async function getChildrenRecursive(parentId, maxDepth, currentDepth) {
124
143
  if (grandchildren.length > 0) {
125
144
  progress = Math.round(grandchildren.reduce((sum, c) => sum + c.progress, 0) / grandchildren.length);
126
145
  }
127
- children.push(dbNodeToTree({ ...child, progress }, (childLinkRows || []).map(dbLinkToLink), grandchildren));
146
+ children.push(dbNodeToTree({ ...child, progress }, linkMap.get(child.id) || [], grandchildren));
128
147
  }
129
148
  return children;
130
149
  }
@@ -305,17 +324,11 @@ export async function getWorkMode() {
305
324
  const nodeIds = wmRows.map(r => r.node_id);
306
325
  const { data: nodeRows } = await supabase
307
326
  .from('nodes')
308
- .select('*')
327
+ .select(NODE_COLUMNS)
309
328
  .in('id', nodeIds);
310
- const results = [];
311
- for (const row of (nodeRows || [])) {
312
- const { data: linkRows } = await supabase
313
- .from('links')
314
- .select('*')
315
- .eq('node_id', row.id);
316
- results.push(dbNodeToTree(row, (linkRows || []).map(dbLinkToLink), []));
317
- }
318
- return results;
329
+ // Batch-fetch all links in ONE query instead of N+1
330
+ const linkMap = await batchGetLinks(nodeIds);
331
+ return (nodeRows || []).map(row => dbNodeToTree(row, linkMap.get(row.id) || [], []));
319
332
  }
320
333
  export async function addToWorkMode(nodeId) {
321
334
  const userId = getUserId();
@@ -469,7 +482,7 @@ export async function removeFromWorkMode(nodeId) {
469
482
  export async function searchNodes(query, filters) {
470
483
  let q = supabase
471
484
  .from('nodes')
472
- .select('*')
485
+ .select(NODE_COLUMNS)
473
486
  .or(`title.ilike.%${query}%,description.ilike.%${query}%`)
474
487
  .order('sort_order', { ascending: true })
475
488
  .limit(50);
@@ -524,7 +537,7 @@ export async function deleteDependency(id) {
524
537
  export async function listDependencies(nodeIds) {
525
538
  const { data, error } = await supabase
526
539
  .from('node_dependencies')
527
- .select('*')
540
+ .select('id,from_node_id,to_node_id,dependency_type,created_by,created_at')
528
541
  .or(`from_node_id.in.(${nodeIds.join(',')}),to_node_id.in.(${nodeIds.join(',')})`);
529
542
  if (error)
530
543
  throw new Error(error.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slyplan-mcp",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
4
4
  "description": "MCP server for Slyplan — visual project management via Claude",
5
5
  "type": "module",
6
6
  "bin": {