@vibescope/mcp-server 0.2.1 → 0.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.
Files changed (93) hide show
  1. package/README.md +60 -7
  2. package/dist/api-client.d.ts +187 -0
  3. package/dist/api-client.js +48 -0
  4. package/dist/handlers/blockers.js +9 -8
  5. package/dist/handlers/bodies-of-work.js +14 -14
  6. package/dist/handlers/connectors.d.ts +45 -0
  7. package/dist/handlers/connectors.js +183 -0
  8. package/dist/handlers/cost.d.ts +10 -0
  9. package/dist/handlers/cost.js +54 -0
  10. package/dist/handlers/decisions.js +3 -3
  11. package/dist/handlers/deployment.js +35 -19
  12. package/dist/handlers/discovery.d.ts +7 -0
  13. package/dist/handlers/discovery.js +61 -2
  14. package/dist/handlers/fallback.js +5 -4
  15. package/dist/handlers/file-checkouts.d.ts +2 -0
  16. package/dist/handlers/file-checkouts.js +38 -6
  17. package/dist/handlers/findings.js +13 -12
  18. package/dist/handlers/git-issues.js +4 -4
  19. package/dist/handlers/ideas.js +5 -5
  20. package/dist/handlers/index.d.ts +1 -0
  21. package/dist/handlers/index.js +3 -0
  22. package/dist/handlers/milestones.js +5 -5
  23. package/dist/handlers/organizations.js +13 -13
  24. package/dist/handlers/progress.js +2 -2
  25. package/dist/handlers/project.js +6 -6
  26. package/dist/handlers/requests.js +3 -3
  27. package/dist/handlers/session.js +28 -9
  28. package/dist/handlers/sprints.js +17 -17
  29. package/dist/handlers/tasks.d.ts +2 -0
  30. package/dist/handlers/tasks.js +78 -20
  31. package/dist/handlers/types.d.ts +64 -2
  32. package/dist/handlers/types.js +48 -1
  33. package/dist/handlers/validation.js +3 -3
  34. package/dist/index.js +7 -2716
  35. package/dist/token-tracking.d.ts +74 -0
  36. package/dist/token-tracking.js +122 -0
  37. package/dist/tools.js +298 -9
  38. package/dist/utils.d.ts +5 -0
  39. package/dist/utils.js +17 -0
  40. package/docs/TOOLS.md +2053 -0
  41. package/package.json +4 -1
  42. package/scripts/generate-docs.ts +212 -0
  43. package/src/api-client.test.ts +718 -0
  44. package/src/api-client.ts +231 -0
  45. package/src/handlers/__test-setup__.ts +9 -0
  46. package/src/handlers/blockers.test.ts +31 -19
  47. package/src/handlers/blockers.ts +9 -8
  48. package/src/handlers/bodies-of-work.test.ts +55 -32
  49. package/src/handlers/bodies-of-work.ts +14 -14
  50. package/src/handlers/connectors.test.ts +834 -0
  51. package/src/handlers/connectors.ts +229 -0
  52. package/src/handlers/cost.ts +66 -0
  53. package/src/handlers/decisions.test.ts +34 -25
  54. package/src/handlers/decisions.ts +3 -3
  55. package/src/handlers/deployment.ts +39 -19
  56. package/src/handlers/discovery.ts +61 -2
  57. package/src/handlers/fallback.test.ts +26 -22
  58. package/src/handlers/fallback.ts +5 -4
  59. package/src/handlers/file-checkouts.test.ts +242 -49
  60. package/src/handlers/file-checkouts.ts +44 -6
  61. package/src/handlers/findings.test.ts +38 -24
  62. package/src/handlers/findings.ts +13 -12
  63. package/src/handlers/git-issues.test.ts +51 -43
  64. package/src/handlers/git-issues.ts +4 -4
  65. package/src/handlers/ideas.test.ts +28 -23
  66. package/src/handlers/ideas.ts +5 -5
  67. package/src/handlers/index.ts +3 -0
  68. package/src/handlers/milestones.test.ts +33 -28
  69. package/src/handlers/milestones.ts +5 -5
  70. package/src/handlers/organizations.test.ts +104 -83
  71. package/src/handlers/organizations.ts +13 -13
  72. package/src/handlers/progress.test.ts +20 -14
  73. package/src/handlers/progress.ts +2 -2
  74. package/src/handlers/project.test.ts +34 -27
  75. package/src/handlers/project.ts +6 -6
  76. package/src/handlers/requests.test.ts +27 -18
  77. package/src/handlers/requests.ts +3 -3
  78. package/src/handlers/session.test.ts +47 -0
  79. package/src/handlers/session.ts +32 -9
  80. package/src/handlers/sprints.test.ts +71 -50
  81. package/src/handlers/sprints.ts +17 -17
  82. package/src/handlers/tasks.test.ts +77 -15
  83. package/src/handlers/tasks.ts +90 -21
  84. package/src/handlers/tool-categories.test.ts +66 -0
  85. package/src/handlers/types.ts +81 -2
  86. package/src/handlers/validation.test.ts +78 -45
  87. package/src/handlers/validation.ts +3 -3
  88. package/src/index.ts +12 -2732
  89. package/src/token-tracking.test.ts +453 -0
  90. package/src/token-tracking.ts +164 -0
  91. package/src/tools.ts +298 -9
  92. package/src/utils.test.ts +2 -2
  93. package/src/utils.ts +17 -0
@@ -6,12 +6,14 @@
6
6
  * - checkin_file: Check in a file after editing
7
7
  * - get_file_checkouts: Get active checkouts for a project
8
8
  * - abandon_checkout: Force release a checkout
9
+ * - is_file_available: Check if a file is available for checkout
9
10
  */
10
11
  import type { Handler, HandlerRegistry } from './types.js';
11
12
  export declare const checkoutFile: Handler;
12
13
  export declare const checkinFile: Handler;
13
14
  export declare const getFileCheckouts: Handler;
14
15
  export declare const abandonCheckout: Handler;
16
+ export declare const isFileAvailable: Handler;
15
17
  /**
16
18
  * File Checkouts handlers registry
17
19
  */
@@ -6,6 +6,7 @@
6
6
  * - checkin_file: Check in a file after editing
7
7
  * - get_file_checkouts: Get active checkouts for a project
8
8
  * - abandon_checkout: Force release a checkout
9
+ * - is_file_available: Check if a file is available for checkout
9
10
  */
10
11
  import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
11
12
  import { getApiClient } from '../api-client.js';
@@ -33,12 +34,16 @@ const abandonCheckoutSchema = {
33
34
  project_id: { type: 'string', validate: uuidValidator },
34
35
  file_path: { type: 'string' },
35
36
  };
37
+ const isFileAvailableSchema = {
38
+ project_id: { type: 'string', required: true, validate: uuidValidator },
39
+ file_path: { type: 'string', required: true },
40
+ };
36
41
  export const checkoutFile = async (args, ctx) => {
37
42
  const { project_id, file_path, reason } = parseArgs(args, checkoutFileSchema);
38
43
  const apiClient = getApiClient();
39
44
  const response = await apiClient.checkoutFile(project_id, file_path, reason, ctx.session.currentSessionId || undefined);
40
45
  if (!response.ok) {
41
- throw new Error(response.error || 'Failed to checkout file');
46
+ return { result: { error: response.error || 'Failed to checkout file' }, isError: true };
42
47
  }
43
48
  return { result: response.data };
44
49
  };
@@ -46,7 +51,7 @@ export const checkinFile = async (args, ctx) => {
46
51
  const { checkout_id, project_id, file_path, summary } = parseArgs(args, checkinFileSchema);
47
52
  // Validate that either checkout_id or both project_id and file_path are provided
48
53
  if (!checkout_id && (!project_id || !file_path)) {
49
- throw new Error('Either checkout_id or both project_id and file_path are required');
54
+ return { result: { error: 'Either checkout_id or both project_id and file_path are required' }, isError: true };
50
55
  }
51
56
  const apiClient = getApiClient();
52
57
  const response = await apiClient.checkinFile({
@@ -56,7 +61,7 @@ export const checkinFile = async (args, ctx) => {
56
61
  summary
57
62
  }, ctx.session.currentSessionId || undefined);
58
63
  if (!response.ok) {
59
- throw new Error(response.error || 'Failed to checkin file');
64
+ return { result: { error: response.error || 'Failed to checkin file' }, isError: true };
60
65
  }
61
66
  return { result: response.data };
62
67
  };
@@ -69,7 +74,7 @@ export const getFileCheckouts = async (args, _ctx) => {
69
74
  limit
70
75
  });
71
76
  if (!response.ok) {
72
- throw new Error(response.error || 'Failed to get file checkouts');
77
+ return { result: { error: response.error || 'Failed to get file checkouts' }, isError: true };
73
78
  }
74
79
  return { result: response.data };
75
80
  };
@@ -77,7 +82,7 @@ export const abandonCheckout = async (args, _ctx) => {
77
82
  const { checkout_id, project_id, file_path } = parseArgs(args, abandonCheckoutSchema);
78
83
  // Validate that either checkout_id or both project_id and file_path are provided
79
84
  if (!checkout_id && (!project_id || !file_path)) {
80
- throw new Error('Either checkout_id or both project_id and file_path are required');
85
+ return { result: { error: 'Either checkout_id or both project_id and file_path are required' }, isError: true };
81
86
  }
82
87
  const apiClient = getApiClient();
83
88
  const response = await apiClient.abandonCheckout({
@@ -86,10 +91,36 @@ export const abandonCheckout = async (args, _ctx) => {
86
91
  file_path
87
92
  });
88
93
  if (!response.ok) {
89
- throw new Error(response.error || 'Failed to abandon checkout');
94
+ return { result: { error: response.error || 'Failed to abandon checkout' }, isError: true };
90
95
  }
91
96
  return { result: response.data };
92
97
  };
98
+ export const isFileAvailable = async (args, _ctx) => {
99
+ const { project_id, file_path } = parseArgs(args, isFileAvailableSchema);
100
+ const apiClient = getApiClient();
101
+ const response = await apiClient.getFileCheckouts(project_id, {
102
+ status: 'checked_out',
103
+ file_path,
104
+ limit: 1
105
+ });
106
+ if (!response.ok) {
107
+ return { result: { error: response.error || 'Failed to check file availability' }, isError: true };
108
+ }
109
+ const checkouts = response.data?.checkouts || [];
110
+ const activeCheckout = checkouts.length > 0 ? checkouts[0] : null;
111
+ return {
112
+ result: {
113
+ available: !activeCheckout,
114
+ file_path,
115
+ checked_out_by: activeCheckout ? {
116
+ checkout_id: activeCheckout.id,
117
+ checked_out_by: activeCheckout.checked_out_by,
118
+ checked_out_at: activeCheckout.checked_out_at,
119
+ reason: activeCheckout.checkout_reason
120
+ } : null
121
+ }
122
+ };
123
+ };
93
124
  /**
94
125
  * File Checkouts handlers registry
95
126
  */
@@ -98,4 +129,5 @@ export const fileCheckoutHandlers = {
98
129
  checkin_file: checkinFile,
99
130
  get_file_checkouts: getFileCheckouts,
100
131
  abandon_checkout: abandonCheckout,
132
+ is_file_available: isFileAvailable,
101
133
  };
@@ -8,6 +8,7 @@
8
8
  * - update_finding
9
9
  * - delete_finding
10
10
  */
11
+ import { success, error } from './types.js';
11
12
  import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
12
13
  import { getApiClient } from '../api-client.js';
13
14
  const VALID_FINDING_CATEGORIES = ['performance', 'security', 'code_quality', 'accessibility', 'documentation', 'architecture', 'testing', 'other'];
@@ -69,9 +70,9 @@ export const addFinding = async (args, ctx) => {
69
70
  related_task_id
70
71
  }, ctx.session.currentSessionId || undefined);
71
72
  if (!response.ok) {
72
- throw new Error(response.error || 'Failed to add finding');
73
+ return error(response.error || 'Failed to add finding');
73
74
  }
74
- return { result: response.data };
75
+ return success(response.data);
75
76
  };
76
77
  export const getFindings = async (args, _ctx) => {
77
78
  const { project_id, category, severity, status, limit, offset, search_query, summary_only } = parseArgs(args, getFindingsSchema);
@@ -86,9 +87,9 @@ export const getFindings = async (args, _ctx) => {
86
87
  summary_only
87
88
  });
88
89
  if (!response.ok) {
89
- throw new Error(response.error || 'Failed to get findings');
90
+ return error(response.error || 'Failed to get findings');
90
91
  }
91
- return { result: response.data };
92
+ return success(response.data);
92
93
  };
93
94
  /**
94
95
  * Get aggregate statistics about findings for a project.
@@ -100,9 +101,9 @@ export const getFindingsStats = async (args, _ctx) => {
100
101
  const apiClient = getApiClient();
101
102
  const response = await apiClient.getFindingsStats(project_id);
102
103
  if (!response.ok) {
103
- throw new Error(response.error || 'Failed to get findings stats');
104
+ return error(response.error || 'Failed to get findings stats');
104
105
  }
105
- return { result: response.data };
106
+ return success(response.data);
106
107
  };
107
108
  export const updateFinding = async (args, _ctx) => {
108
109
  const { finding_id, title, description, severity, status, resolution_note } = parseArgs(args, updateFindingSchema);
@@ -115,18 +116,18 @@ export const updateFinding = async (args, _ctx) => {
115
116
  resolution_note
116
117
  });
117
118
  if (!response.ok) {
118
- throw new Error(response.error || 'Failed to update finding');
119
+ return error(response.error || 'Failed to update finding');
119
120
  }
120
- return { result: response.data };
121
+ return success(response.data);
121
122
  };
122
123
  export const deleteFinding = async (args, _ctx) => {
123
124
  const { finding_id } = parseArgs(args, deleteFindingSchema);
124
125
  const apiClient = getApiClient();
125
126
  const response = await apiClient.deleteFinding(finding_id);
126
127
  if (!response.ok) {
127
- throw new Error(response.error || 'Failed to delete finding');
128
+ return error(response.error || 'Failed to delete finding');
128
129
  }
129
- return { result: response.data };
130
+ return success(response.data);
130
131
  };
131
132
  /**
132
133
  * Query aggregated project knowledge in a single call.
@@ -145,9 +146,9 @@ export const queryKnowledgeBase = async (args, _ctx) => {
145
146
  search_query
146
147
  });
147
148
  if (!response.ok) {
148
- throw new Error(response.error || 'Failed to query knowledge base');
149
+ return error(response.error || 'Failed to query knowledge base');
149
150
  }
150
- return { result: response.data };
151
+ return success(response.data);
151
152
  };
152
153
  /**
153
154
  * Findings handlers registry
@@ -56,7 +56,7 @@ export const addGitIssue = async (args, ctx) => {
56
56
  task_id
57
57
  }, ctx.session.currentSessionId || undefined);
58
58
  if (!response.ok) {
59
- throw new Error(response.error || 'Failed to add git issue');
59
+ return { result: { error: response.error || 'Failed to add git issue' }, isError: true };
60
60
  }
61
61
  return { result: response.data };
62
62
  };
@@ -68,7 +68,7 @@ export const resolveGitIssue = async (args, ctx) => {
68
68
  auto_resolved
69
69
  }, ctx.session.currentSessionId || undefined);
70
70
  if (!response.ok) {
71
- throw new Error(response.error || 'Failed to resolve git issue');
71
+ return { result: { error: response.error || 'Failed to resolve git issue' }, isError: true };
72
72
  }
73
73
  return { result: response.data };
74
74
  };
@@ -82,7 +82,7 @@ export const getGitIssues = async (args, _ctx) => {
82
82
  limit
83
83
  });
84
84
  if (!response.ok) {
85
- throw new Error(response.error || 'Failed to fetch git issues');
85
+ return { result: { error: response.error || 'Failed to fetch git issues' }, isError: true };
86
86
  }
87
87
  return { result: response.data };
88
88
  };
@@ -91,7 +91,7 @@ export const deleteGitIssue = async (args, _ctx) => {
91
91
  const apiClient = getApiClient();
92
92
  const response = await apiClient.deleteGitIssue(git_issue_id);
93
93
  if (!response.ok) {
94
- throw new Error(response.error || 'Failed to delete git issue');
94
+ return { result: { error: response.error || 'Failed to delete git issue' }, isError: true };
95
95
  }
96
96
  return { result: response.data };
97
97
  };
@@ -53,7 +53,7 @@ export const addIdea = async (args, ctx) => {
53
53
  status: status
54
54
  }, session.currentSessionId || undefined);
55
55
  if (!response.ok) {
56
- throw new Error(`Failed to add idea: ${response.error}`);
56
+ return { result: { error: response.error || 'Failed to add idea' }, isError: true };
57
57
  }
58
58
  return { result: { success: true, idea_id: response.data?.idea_id, title } };
59
59
  };
@@ -67,7 +67,7 @@ export const updateIdea = async (args, _ctx) => {
67
67
  doc_url
68
68
  });
69
69
  if (!response.ok) {
70
- throw new Error(`Failed to update idea: ${response.error}`);
70
+ return { result: { error: response.error || 'Failed to update idea' }, isError: true };
71
71
  }
72
72
  return { result: { success: true, idea_id } };
73
73
  };
@@ -81,7 +81,7 @@ export const getIdeas = async (args, _ctx) => {
81
81
  search_query
82
82
  });
83
83
  if (!response.ok) {
84
- throw new Error(`Failed to fetch ideas: ${response.error}`);
84
+ return { result: { error: response.error || 'Failed to fetch ideas' }, isError: true };
85
85
  }
86
86
  return {
87
87
  result: {
@@ -94,7 +94,7 @@ export const deleteIdea = async (args, _ctx) => {
94
94
  const apiClient = getApiClient();
95
95
  const response = await apiClient.deleteIdea(idea_id);
96
96
  if (!response.ok) {
97
- throw new Error(`Failed to delete idea: ${response.error}`);
97
+ return { result: { error: response.error || 'Failed to delete idea' }, isError: true };
98
98
  }
99
99
  return { result: { success: true } };
100
100
  };
@@ -109,7 +109,7 @@ export const convertIdeaToTask = async (args, _ctx) => {
109
109
  update_status
110
110
  });
111
111
  if (!response.ok) {
112
- throw new Error(`Failed to convert idea: ${response.error}`);
112
+ return { result: { error: response.error || 'Failed to convert idea' }, isError: true };
113
113
  }
114
114
  return { result: response.data };
115
115
  };
@@ -26,6 +26,7 @@ export * from './git-issues.js';
26
26
  export * from './sprints.js';
27
27
  export * from './file-checkouts.js';
28
28
  export * from './roles.js';
29
+ export * from './connectors.js';
29
30
  import type { HandlerRegistry } from './types.js';
30
31
  /**
31
32
  * Build the complete handler registry from all modules
@@ -26,6 +26,7 @@ export * from './git-issues.js';
26
26
  export * from './sprints.js';
27
27
  export * from './file-checkouts.js';
28
28
  export * from './roles.js';
29
+ export * from './connectors.js';
29
30
  import { milestoneHandlers } from './milestones.js';
30
31
  import { sessionHandlers } from './session.js';
31
32
  import { ideaHandlers } from './ideas.js';
@@ -47,6 +48,7 @@ import { gitIssueHandlers } from './git-issues.js';
47
48
  import { sprintHandlers } from './sprints.js';
48
49
  import { fileCheckoutHandlers } from './file-checkouts.js';
49
50
  import { roleHandlers } from './roles.js';
51
+ import { connectorHandlers } from './connectors.js';
50
52
  /**
51
53
  * Build the complete handler registry from all modules
52
54
  */
@@ -73,5 +75,6 @@ export function buildHandlerRegistry() {
73
75
  ...sprintHandlers,
74
76
  ...fileCheckoutHandlers,
75
77
  ...roleHandlers,
78
+ ...connectorHandlers,
76
79
  };
77
80
  }
@@ -46,7 +46,7 @@ export const addMilestone = async (args, ctx) => {
46
46
  order_index
47
47
  }, session.currentSessionId || undefined);
48
48
  if (!response.ok) {
49
- throw new Error(`Failed to add milestone: ${response.error}`);
49
+ return { result: { error: response.error || 'Failed to add milestone' }, isError: true };
50
50
  }
51
51
  return {
52
52
  result: {
@@ -69,7 +69,7 @@ export const updateMilestone = async (args, _ctx) => {
69
69
  order_index
70
70
  });
71
71
  if (!response.ok) {
72
- throw new Error(`Failed to update milestone: ${response.error}`);
72
+ return { result: { error: response.error || 'Failed to update milestone' }, isError: true };
73
73
  }
74
74
  return {
75
75
  result: {
@@ -83,7 +83,7 @@ export const completeMilestone = async (args, _ctx) => {
83
83
  const apiClient = getApiClient();
84
84
  const response = await apiClient.completeMilestone(milestone_id);
85
85
  if (!response.ok) {
86
- throw new Error(`Failed to complete milestone: ${response.error}`);
86
+ return { result: { error: response.error || 'Failed to complete milestone' }, isError: true };
87
87
  }
88
88
  return {
89
89
  result: {
@@ -97,7 +97,7 @@ export const deleteMilestone = async (args, _ctx) => {
97
97
  const apiClient = getApiClient();
98
98
  const response = await apiClient.deleteMilestone(milestone_id);
99
99
  if (!response.ok) {
100
- throw new Error(`Failed to delete milestone: ${response.error}`);
100
+ return { result: { error: response.error || 'Failed to delete milestone' }, isError: true };
101
101
  }
102
102
  return {
103
103
  result: {
@@ -111,7 +111,7 @@ export const getMilestones = async (args, _ctx) => {
111
111
  const apiClient = getApiClient();
112
112
  const response = await apiClient.getMilestones(task_id);
113
113
  if (!response.ok) {
114
- throw new Error(`Failed to get milestones: ${response.error}`);
114
+ return { result: { error: response.error || 'Failed to get milestones' }, isError: true };
115
115
  }
116
116
  // Stats are calculated server-side now
117
117
  return {
@@ -84,7 +84,7 @@ export const listOrganizations = async (_args, _ctx) => {
84
84
  const apiClient = getApiClient();
85
85
  const response = await apiClient.listOrganizations();
86
86
  if (!response.ok) {
87
- throw new Error(response.error || 'Failed to list organizations');
87
+ return { result: { error: response.error || 'Failed to list organizations' }, isError: true };
88
88
  }
89
89
  return { result: response.data };
90
90
  };
@@ -97,7 +97,7 @@ export const createOrganization = async (args, _ctx) => {
97
97
  slug
98
98
  });
99
99
  if (!response.ok) {
100
- throw new Error(response.error || 'Failed to create organization');
100
+ return { result: { error: response.error || 'Failed to create organization' }, isError: true };
101
101
  }
102
102
  return { result: response.data };
103
103
  };
@@ -117,7 +117,7 @@ export const updateOrganization = async (args, _ctx) => {
117
117
  const apiClient = getApiClient();
118
118
  const response = await apiClient.updateOrganization(organization_id, updates);
119
119
  if (!response.ok) {
120
- throw new Error(response.error || 'Failed to update organization');
120
+ return { result: { error: response.error || 'Failed to update organization' }, isError: true };
121
121
  }
122
122
  return { result: response.data };
123
123
  };
@@ -126,7 +126,7 @@ export const deleteOrganization = async (args, _ctx) => {
126
126
  const apiClient = getApiClient();
127
127
  const response = await apiClient.deleteOrganization(organization_id);
128
128
  if (!response.ok) {
129
- throw new Error(response.error || 'Failed to delete organization');
129
+ return { result: { error: response.error || 'Failed to delete organization' }, isError: true };
130
130
  }
131
131
  return { result: response.data };
132
132
  };
@@ -138,7 +138,7 @@ export const listOrgMembers = async (args, _ctx) => {
138
138
  const apiClient = getApiClient();
139
139
  const response = await apiClient.listOrgMembers(organization_id);
140
140
  if (!response.ok) {
141
- throw new Error(response.error || 'Failed to list members');
141
+ return { result: { error: response.error || 'Failed to list members' }, isError: true };
142
142
  }
143
143
  return { result: response.data };
144
144
  };
@@ -147,7 +147,7 @@ export const inviteMember = async (args, _ctx) => {
147
147
  const apiClient = getApiClient();
148
148
  const response = await apiClient.inviteMember(organization_id, email, role);
149
149
  if (!response.ok) {
150
- throw new Error(response.error || 'Failed to create invite');
150
+ return { result: { error: response.error || 'Failed to create invite' }, isError: true };
151
151
  }
152
152
  return { result: response.data };
153
153
  };
@@ -159,7 +159,7 @@ export const updateMemberRole = async (args, _ctx) => {
159
159
  const apiClient = getApiClient();
160
160
  const response = await apiClient.updateMemberRole(organization_id, user_id, role);
161
161
  if (!response.ok) {
162
- throw new Error(response.error || 'Failed to update member role');
162
+ return { result: { error: response.error || 'Failed to update member role' }, isError: true };
163
163
  }
164
164
  return { result: response.data };
165
165
  };
@@ -168,7 +168,7 @@ export const removeMember = async (args, _ctx) => {
168
168
  const apiClient = getApiClient();
169
169
  const response = await apiClient.removeMember(organization_id, user_id);
170
170
  if (!response.ok) {
171
- throw new Error(response.error || 'Failed to remove member');
171
+ return { result: { error: response.error || 'Failed to remove member' }, isError: true };
172
172
  }
173
173
  return { result: response.data };
174
174
  };
@@ -177,7 +177,7 @@ export const leaveOrganization = async (args, _ctx) => {
177
177
  const apiClient = getApiClient();
178
178
  const response = await apiClient.leaveOrganization(organization_id);
179
179
  if (!response.ok) {
180
- throw new Error(response.error || 'Failed to leave organization');
180
+ return { result: { error: response.error || 'Failed to leave organization' }, isError: true };
181
181
  }
182
182
  return { result: response.data };
183
183
  };
@@ -189,7 +189,7 @@ export const shareProjectWithOrg = async (args, _ctx) => {
189
189
  const apiClient = getApiClient();
190
190
  const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission);
191
191
  if (!response.ok) {
192
- throw new Error(response.error || 'Failed to share project');
192
+ return { result: { error: response.error || 'Failed to share project' }, isError: true };
193
193
  }
194
194
  return { result: response.data };
195
195
  };
@@ -198,7 +198,7 @@ export const updateProjectShare = async (args, _ctx) => {
198
198
  const apiClient = getApiClient();
199
199
  const response = await apiClient.updateProjectShare(project_id, organization_id, permission);
200
200
  if (!response.ok) {
201
- throw new Error(response.error || 'Failed to update share');
201
+ return { result: { error: response.error || 'Failed to update share' }, isError: true };
202
202
  }
203
203
  return { result: response.data };
204
204
  };
@@ -207,7 +207,7 @@ export const unshareProject = async (args, _ctx) => {
207
207
  const apiClient = getApiClient();
208
208
  const response = await apiClient.unshareProject(project_id, organization_id);
209
209
  if (!response.ok) {
210
- throw new Error(response.error || 'Failed to unshare project');
210
+ return { result: { error: response.error || 'Failed to unshare project' }, isError: true };
211
211
  }
212
212
  return { result: response.data };
213
213
  };
@@ -216,7 +216,7 @@ export const listProjectShares = async (args, _ctx) => {
216
216
  const apiClient = getApiClient();
217
217
  const response = await apiClient.listProjectShares(project_id);
218
218
  if (!response.ok) {
219
- throw new Error(response.error || 'Failed to list shares');
219
+ return { result: { error: response.error || 'Failed to list shares' }, isError: true };
220
220
  }
221
221
  return { result: response.data };
222
222
  };
@@ -34,7 +34,7 @@ export const logProgress = async (args, ctx) => {
34
34
  session_id: session.currentSessionId || undefined
35
35
  });
36
36
  if (!response.ok) {
37
- throw new Error(`Failed to log progress: ${response.error}`);
37
+ return { result: { error: response.error || 'Failed to log progress' }, isError: true };
38
38
  }
39
39
  return { result: { success: true, progress_id: response.data?.progress_id } };
40
40
  };
@@ -49,7 +49,7 @@ export const getActivityFeed = async (args, _ctx) => {
49
49
  created_by
50
50
  });
51
51
  if (!response.ok) {
52
- throw new Error(`Failed to fetch activity feed: ${response.error}`);
52
+ return { result: { error: response.error || 'Failed to fetch activity feed' }, isError: true };
53
53
  }
54
54
  return { result: { activities: response.data?.activities || [] } };
55
55
  };
@@ -53,14 +53,14 @@ export const getProjectContext = async (args, _ctx) => {
53
53
  if (!project_id && !git_url) {
54
54
  const response = await apiClient.listProjects();
55
55
  if (!response.ok) {
56
- throw new Error(response.error || 'Failed to fetch projects');
56
+ return { result: { error: response.error || 'Failed to fetch projects' }, isError: true };
57
57
  }
58
58
  return { result: { projects: response.data?.projects || [] } };
59
59
  }
60
60
  // Find project by ID or git_url
61
61
  const response = await apiClient.getProject(project_id || 'by-git-url', git_url);
62
62
  if (!response.ok) {
63
- throw new Error(response.error || 'Failed to fetch project');
63
+ return { result: { error: response.error || 'Failed to fetch project' }, isError: true };
64
64
  }
65
65
  if (!response.data?.found) {
66
66
  return {
@@ -77,7 +77,7 @@ export const getGitWorkflow = async (args, _ctx) => {
77
77
  const apiClient = getApiClient();
78
78
  const response = await apiClient.getGitWorkflow(project_id, task_id);
79
79
  if (!response.ok) {
80
- throw new Error(response.error || 'Failed to get git workflow');
80
+ return { result: { error: response.error || 'Failed to get git workflow' }, isError: true };
81
81
  }
82
82
  return { result: response.data };
83
83
  };
@@ -92,7 +92,7 @@ export const createProject = async (args, _ctx) => {
92
92
  tech_stack: tech_stack
93
93
  });
94
94
  if (!response.ok) {
95
- throw new Error(response.error || 'Failed to create project');
95
+ return { result: { error: response.error || 'Failed to create project' }, isError: true };
96
96
  }
97
97
  return { result: response.data };
98
98
  };
@@ -114,7 +114,7 @@ export const updateProject = async (args, _ctx) => {
114
114
  deployment_instructions
115
115
  });
116
116
  if (!response.ok) {
117
- throw new Error(response.error || 'Failed to update project');
117
+ return { result: { error: response.error || 'Failed to update project' }, isError: true };
118
118
  }
119
119
  return { result: response.data };
120
120
  };
@@ -123,7 +123,7 @@ export const updateProjectReadme = async (args, _ctx) => {
123
123
  const apiClient = getApiClient();
124
124
  const response = await apiClient.updateProjectReadme(project_id, readme_content);
125
125
  if (!response.ok) {
126
- throw new Error(response.error || 'Failed to update README');
126
+ return { result: { error: response.error || 'Failed to update README' }, isError: true };
127
127
  }
128
128
  return { result: response.data };
129
129
  };
@@ -27,7 +27,7 @@ export const getPendingRequests = async (args, ctx) => {
27
27
  const apiClient = getApiClient();
28
28
  const response = await apiClient.getPendingRequests(project_id, session.currentSessionId || undefined);
29
29
  if (!response.ok) {
30
- throw new Error(`Failed to get pending requests: ${response.error}`);
30
+ return { result: { error: response.error || 'Failed to get pending requests' }, isError: true };
31
31
  }
32
32
  return {
33
33
  result: {
@@ -42,7 +42,7 @@ export const acknowledgeRequest = async (args, ctx) => {
42
42
  const apiClient = getApiClient();
43
43
  const response = await apiClient.acknowledgeRequest(request_id, session.currentSessionId || undefined);
44
44
  if (!response.ok) {
45
- throw new Error(`Failed to acknowledge request: ${response.error}`);
45
+ return { result: { error: response.error || 'Failed to acknowledge request' }, isError: true };
46
46
  }
47
47
  return {
48
48
  result: {
@@ -56,7 +56,7 @@ export const answerQuestion = async (args, ctx) => {
56
56
  const apiClient = getApiClient();
57
57
  const response = await apiClient.answerQuestion(request_id, answer, session.currentSessionId || undefined);
58
58
  if (!response.ok) {
59
- throw new Error(`Failed to answer question: ${response.error}`);
59
+ return { result: { error: response.error || 'Failed to answer question' }, isError: true };
60
60
  }
61
61
  return {
62
62
  result: {
@@ -83,20 +83,33 @@ export const startWorkSession = async (args, ctx) => {
83
83
  currentPersona: data.persona || null,
84
84
  });
85
85
  }
86
- // Build result with directive at top for visibility
86
+ // Check for urgent questions - these MUST be handled first
87
+ const hasUrgentQuestions = data.URGENT_QUESTIONS || (data.pending_requests && data.pending_requests.length > 0);
88
+ // Build result - URGENT_QUESTIONS at absolute top for maximum visibility
87
89
  const result = {
88
90
  session_started: true,
89
- directive: data.directive || 'ACTION_REQUIRED: Start working immediately.',
90
- auto_continue: true,
91
- session_id: data.session_id,
92
- persona: data.persona,
93
- role: data.role,
94
- project: data.project,
95
91
  };
92
+ // URGENT_QUESTIONS must be the FIRST thing the agent sees
93
+ if (data.URGENT_QUESTIONS) {
94
+ result.URGENT_QUESTIONS = data.URGENT_QUESTIONS;
95
+ }
96
+ // Directive comes right after urgent questions
97
+ result.directive = data.directive || 'ACTION_REQUIRED: Start working immediately.';
98
+ result.auto_continue = true;
99
+ // Session info
100
+ result.session_id = data.session_id;
101
+ result.persona = data.persona;
102
+ result.role = data.role;
103
+ result.project = data.project;
96
104
  // Add task data
97
105
  if (data.next_task) {
98
106
  result.next_task = data.next_task;
99
107
  }
108
+ // Add pending requests (questions from user) - these take priority
109
+ if (data.pending_requests && data.pending_requests.length > 0) {
110
+ result.pending_requests = data.pending_requests;
111
+ result.pending_requests_count = data.pending_requests.length;
112
+ }
100
113
  // Add active tasks for full mode
101
114
  if (data.active_tasks) {
102
115
  result.active_tasks = data.active_tasks;
@@ -125,8 +138,14 @@ export const startWorkSession = async (args, ctx) => {
125
138
  worktree_hint: 'CRITICAL: Create a git worktree before starting work. Run get_help("git") for instructions.',
126
139
  };
127
140
  }
128
- // Add next action at end
129
- if (data.next_task) {
141
+ // Add next action at end - pending requests take priority over tasks
142
+ if (hasUrgentQuestions) {
143
+ const firstQuestion = data.URGENT_QUESTIONS?.requests?.[0] || data.pending_requests?.[0];
144
+ result.next_action = firstQuestion
145
+ ? `answer_question(request_id: "${firstQuestion.id}", answer: "...")`
146
+ : 'Check pending_requests and respond using answer_question(request_id, answer)';
147
+ }
148
+ else if (data.next_task) {
130
149
  result.next_action = `update_task(task_id: "${data.next_task.id}", status: "in_progress")`;
131
150
  }
132
151
  else if (data.project) {