@vibescope/mcp-server 0.2.0 → 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 (104) hide show
  1. package/README.md +60 -7
  2. package/dist/api-client.d.ts +251 -1
  3. package/dist/api-client.js +82 -3
  4. package/dist/handlers/blockers.js +9 -8
  5. package/dist/handlers/bodies-of-work.js +96 -63
  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 +112 -50
  10. package/dist/handlers/decisions.js +32 -19
  11. package/dist/handlers/deployment.js +144 -122
  12. package/dist/handlers/discovery.d.ts +7 -0
  13. package/dist/handlers/discovery.js +96 -7
  14. package/dist/handlers/fallback.js +29 -23
  15. package/dist/handlers/file-checkouts.d.ts +20 -0
  16. package/dist/handlers/file-checkouts.js +133 -0
  17. package/dist/handlers/findings.d.ts +6 -0
  18. package/dist/handlers/findings.js +96 -40
  19. package/dist/handlers/git-issues.js +40 -36
  20. package/dist/handlers/ideas.js +49 -31
  21. package/dist/handlers/index.d.ts +3 -0
  22. package/dist/handlers/index.js +9 -0
  23. package/dist/handlers/milestones.js +39 -32
  24. package/dist/handlers/organizations.js +99 -91
  25. package/dist/handlers/progress.js +24 -13
  26. package/dist/handlers/project.js +68 -28
  27. package/dist/handlers/requests.js +18 -14
  28. package/dist/handlers/roles.d.ts +18 -0
  29. package/dist/handlers/roles.js +130 -0
  30. package/dist/handlers/session.js +58 -17
  31. package/dist/handlers/sprints.js +93 -81
  32. package/dist/handlers/tasks.d.ts +2 -0
  33. package/dist/handlers/tasks.js +189 -91
  34. package/dist/handlers/types.d.ts +64 -2
  35. package/dist/handlers/types.js +48 -1
  36. package/dist/handlers/validation.js +21 -17
  37. package/dist/index.js +7 -2716
  38. package/dist/token-tracking.d.ts +74 -0
  39. package/dist/token-tracking.js +122 -0
  40. package/dist/tools.js +685 -9
  41. package/dist/utils.d.ts +5 -0
  42. package/dist/utils.js +17 -0
  43. package/docs/TOOLS.md +2053 -0
  44. package/package.json +4 -1
  45. package/scripts/generate-docs.ts +212 -0
  46. package/src/api-client.test.ts +718 -0
  47. package/src/api-client.ts +320 -6
  48. package/src/handlers/__test-setup__.ts +16 -0
  49. package/src/handlers/blockers.test.ts +31 -19
  50. package/src/handlers/blockers.ts +9 -8
  51. package/src/handlers/bodies-of-work.test.ts +55 -32
  52. package/src/handlers/bodies-of-work.ts +115 -115
  53. package/src/handlers/connectors.test.ts +834 -0
  54. package/src/handlers/connectors.ts +229 -0
  55. package/src/handlers/cost.test.ts +34 -44
  56. package/src/handlers/cost.ts +136 -85
  57. package/src/handlers/decisions.test.ts +37 -27
  58. package/src/handlers/decisions.ts +35 -30
  59. package/src/handlers/deployment.ts +180 -208
  60. package/src/handlers/discovery.test.ts +4 -5
  61. package/src/handlers/discovery.ts +98 -8
  62. package/src/handlers/fallback.test.ts +26 -22
  63. package/src/handlers/fallback.ts +36 -33
  64. package/src/handlers/file-checkouts.test.ts +670 -0
  65. package/src/handlers/file-checkouts.ts +165 -0
  66. package/src/handlers/findings.test.ts +178 -19
  67. package/src/handlers/findings.ts +112 -74
  68. package/src/handlers/git-issues.test.ts +51 -43
  69. package/src/handlers/git-issues.ts +44 -84
  70. package/src/handlers/ideas.test.ts +28 -23
  71. package/src/handlers/ideas.ts +61 -59
  72. package/src/handlers/index.ts +9 -0
  73. package/src/handlers/milestones.test.ts +33 -28
  74. package/src/handlers/milestones.ts +52 -50
  75. package/src/handlers/organizations.test.ts +104 -83
  76. package/src/handlers/organizations.ts +117 -142
  77. package/src/handlers/progress.test.ts +20 -14
  78. package/src/handlers/progress.ts +26 -24
  79. package/src/handlers/project.test.ts +34 -27
  80. package/src/handlers/project.ts +95 -63
  81. package/src/handlers/requests.test.ts +27 -18
  82. package/src/handlers/requests.ts +21 -17
  83. package/src/handlers/roles.test.ts +303 -0
  84. package/src/handlers/roles.ts +208 -0
  85. package/src/handlers/session.test.ts +47 -0
  86. package/src/handlers/session.ts +71 -26
  87. package/src/handlers/sprints.test.ts +71 -50
  88. package/src/handlers/sprints.ts +113 -146
  89. package/src/handlers/tasks.test.ts +77 -15
  90. package/src/handlers/tasks.ts +231 -156
  91. package/src/handlers/tool-categories.test.ts +66 -0
  92. package/src/handlers/types.ts +81 -2
  93. package/src/handlers/validation.test.ts +78 -45
  94. package/src/handlers/validation.ts +23 -25
  95. package/src/index.ts +12 -2732
  96. package/src/token-tracking.test.ts +453 -0
  97. package/src/token-tracking.ts +164 -0
  98. package/src/tools.ts +685 -9
  99. package/src/utils.test.ts +2 -2
  100. package/src/utils.ts +17 -0
  101. package/dist/config/tool-categories.d.ts +0 -31
  102. package/dist/config/tool-categories.js +0 -253
  103. package/dist/knowledge.d.ts +0 -6
  104. package/dist/knowledge.js +0 -218
@@ -7,13 +7,24 @@
7
7
  *
8
8
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
9
9
  */
10
- import { validateRequired, validateUUID } from '../validators.js';
10
+ import { parseArgs, uuidValidator } from '../validators.js';
11
11
  import { getApiClient } from '../api-client.js';
12
+ // Argument schemas for type-safe parsing
13
+ const logProgressSchema = {
14
+ project_id: { type: 'string', required: true, validate: uuidValidator },
15
+ task_id: { type: 'string', validate: uuidValidator },
16
+ summary: { type: 'string', required: true },
17
+ details: { type: 'string' },
18
+ };
19
+ const getActivityFeedSchema = {
20
+ project_id: { type: 'string', required: true, validate: uuidValidator },
21
+ limit: { type: 'number', default: 50 },
22
+ since: { type: 'string' },
23
+ types: { type: 'array' },
24
+ created_by: { type: 'string' },
25
+ };
12
26
  export const logProgress = async (args, ctx) => {
13
- const { project_id, task_id, summary, details } = args;
14
- validateRequired(project_id, 'project_id');
15
- validateUUID(project_id, 'project_id');
16
- validateRequired(summary, 'summary');
27
+ const { project_id, task_id, summary, details } = parseArgs(args, logProgressSchema);
17
28
  const { session } = ctx;
18
29
  const apiClient = getApiClient();
19
30
  const response = await apiClient.logProgress(project_id, {
@@ -23,22 +34,22 @@ export const logProgress = async (args, ctx) => {
23
34
  session_id: session.currentSessionId || undefined
24
35
  });
25
36
  if (!response.ok) {
26
- throw new Error(`Failed to log progress: ${response.error}`);
37
+ return { result: { error: response.error || 'Failed to log progress' }, isError: true };
27
38
  }
28
39
  return { result: { success: true, progress_id: response.data?.progress_id } };
29
40
  };
30
- export const getActivityFeed = async (args, ctx) => {
31
- const { project_id, limit = 50, since } = args;
32
- validateRequired(project_id, 'project_id');
33
- validateUUID(project_id, 'project_id');
41
+ export const getActivityFeed = async (args, _ctx) => {
42
+ const { project_id, limit, since, types, created_by } = parseArgs(args, getActivityFeedSchema);
34
43
  const apiClient = getApiClient();
35
- const effectiveLimit = Math.min(limit, 200);
44
+ const effectiveLimit = Math.min(limit ?? 50, 200);
36
45
  const response = await apiClient.getActivityFeed(project_id, {
37
46
  limit: effectiveLimit,
38
- since
47
+ since,
48
+ types: types,
49
+ created_by
39
50
  });
40
51
  if (!response.ok) {
41
- throw new Error(`Failed to fetch activity feed: ${response.error}`);
52
+ return { result: { error: response.error || 'Failed to fetch activity feed' }, isError: true };
42
53
  }
43
54
  return { result: { activities: response.data?.activities || [] } };
44
55
  };
@@ -8,23 +8,59 @@
8
8
  * - update_project
9
9
  * - update_project_readme
10
10
  */
11
- import { validateRequired, validateUUID, validateProjectStatus } from '../validators.js';
11
+ import { parseArgs, uuidValidator, projectStatusValidator, createEnumValidator, } from '../validators.js';
12
12
  import { getApiClient } from '../api-client.js';
13
- export const getProjectContext = async (args, ctx) => {
14
- const { project_id, git_url } = args;
13
+ const VALID_GIT_WORKFLOWS = ['none', 'trunk-based', 'github-flow', 'git-flow'];
14
+ // Argument schemas for type-safe parsing
15
+ const getProjectContextSchema = {
16
+ project_id: { type: 'string', validate: uuidValidator },
17
+ git_url: { type: 'string' },
18
+ };
19
+ const getGitWorkflowSchema = {
20
+ project_id: { type: 'string', required: true, validate: uuidValidator },
21
+ task_id: { type: 'string', validate: uuidValidator },
22
+ };
23
+ const createProjectSchema = {
24
+ name: { type: 'string', required: true },
25
+ description: { type: 'string' },
26
+ goal: { type: 'string' },
27
+ git_url: { type: 'string' },
28
+ tech_stack: { type: 'array' },
29
+ };
30
+ const updateProjectSchema = {
31
+ project_id: { type: 'string', required: true, validate: uuidValidator },
32
+ name: { type: 'string' },
33
+ description: { type: 'string' },
34
+ goal: { type: 'string' },
35
+ git_url: { type: 'string' },
36
+ tech_stack: { type: 'array' },
37
+ status: { type: 'string', validate: projectStatusValidator },
38
+ git_workflow: { type: 'string', validate: createEnumValidator(VALID_GIT_WORKFLOWS) },
39
+ git_main_branch: { type: 'string' },
40
+ git_develop_branch: { type: 'string' },
41
+ git_auto_branch: { type: 'boolean' },
42
+ git_auto_tag: { type: 'boolean' },
43
+ deployment_instructions: { type: 'string' },
44
+ };
45
+ const updateProjectReadmeSchema = {
46
+ project_id: { type: 'string', required: true, validate: uuidValidator },
47
+ readme_content: { type: 'string', required: true },
48
+ };
49
+ export const getProjectContext = async (args, _ctx) => {
50
+ const { project_id, git_url } = parseArgs(args, getProjectContextSchema);
15
51
  const apiClient = getApiClient();
16
52
  // If no project_id or git_url, list all projects
17
53
  if (!project_id && !git_url) {
18
54
  const response = await apiClient.listProjects();
19
55
  if (!response.ok) {
20
- throw new Error(response.error || 'Failed to fetch projects');
56
+ return { result: { error: response.error || 'Failed to fetch projects' }, isError: true };
21
57
  }
22
58
  return { result: { projects: response.data?.projects || [] } };
23
59
  }
24
60
  // Find project by ID or git_url
25
61
  const response = await apiClient.getProject(project_id || 'by-git-url', git_url);
26
62
  if (!response.ok) {
27
- throw new Error(response.error || 'Failed to fetch project');
63
+ return { result: { error: response.error || 'Failed to fetch project' }, isError: true };
28
64
  }
29
65
  if (!response.data?.found) {
30
66
  return {
@@ -36,54 +72,58 @@ export const getProjectContext = async (args, ctx) => {
36
72
  }
37
73
  return { result: response.data };
38
74
  };
39
- export const getGitWorkflow = async (args, ctx) => {
40
- const { project_id, task_id } = args;
41
- validateRequired(project_id, 'project_id');
42
- validateUUID(project_id, 'project_id');
75
+ export const getGitWorkflow = async (args, _ctx) => {
76
+ const { project_id, task_id } = parseArgs(args, getGitWorkflowSchema);
43
77
  const apiClient = getApiClient();
44
78
  const response = await apiClient.getGitWorkflow(project_id, task_id);
45
79
  if (!response.ok) {
46
- throw new Error(response.error || 'Failed to get git workflow');
80
+ return { result: { error: response.error || 'Failed to get git workflow' }, isError: true };
47
81
  }
48
82
  return { result: response.data };
49
83
  };
50
- export const createProject = async (args, ctx) => {
51
- const { name, description, goal, git_url, tech_stack } = args;
52
- validateRequired(name, 'name');
84
+ export const createProject = async (args, _ctx) => {
85
+ const { name, description, goal, git_url, tech_stack } = parseArgs(args, createProjectSchema);
53
86
  const apiClient = getApiClient();
54
87
  const response = await apiClient.createProject({
55
88
  name,
56
89
  description,
57
90
  goal,
58
91
  git_url,
59
- tech_stack
92
+ tech_stack: tech_stack
60
93
  });
61
94
  if (!response.ok) {
62
- throw new Error(response.error || 'Failed to create project');
95
+ return { result: { error: response.error || 'Failed to create project' }, isError: true };
63
96
  }
64
97
  return { result: response.data };
65
98
  };
66
- export const updateProject = async (args, ctx) => {
67
- const { project_id, ...updates } = args;
68
- validateRequired(project_id, 'project_id');
69
- validateUUID(project_id, 'project_id');
70
- validateProjectStatus(updates.status);
99
+ export const updateProject = async (args, _ctx) => {
100
+ const { project_id, name, description, goal, git_url, tech_stack, status, git_workflow, git_main_branch, git_develop_branch, git_auto_branch, git_auto_tag, deployment_instructions } = parseArgs(args, updateProjectSchema);
71
101
  const apiClient = getApiClient();
72
- const response = await apiClient.updateProject(project_id, updates);
102
+ const response = await apiClient.updateProject(project_id, {
103
+ name,
104
+ description,
105
+ goal,
106
+ git_url,
107
+ tech_stack: tech_stack,
108
+ status: status,
109
+ git_workflow: git_workflow,
110
+ git_main_branch,
111
+ git_develop_branch,
112
+ git_auto_branch,
113
+ git_auto_tag,
114
+ deployment_instructions
115
+ });
73
116
  if (!response.ok) {
74
- throw new Error(response.error || 'Failed to update project');
117
+ return { result: { error: response.error || 'Failed to update project' }, isError: true };
75
118
  }
76
119
  return { result: response.data };
77
120
  };
78
- export const updateProjectReadme = async (args, ctx) => {
79
- const { project_id, readme_content } = args;
80
- validateRequired(project_id, 'project_id');
81
- validateUUID(project_id, 'project_id');
82
- validateRequired(readme_content, 'readme_content');
121
+ export const updateProjectReadme = async (args, _ctx) => {
122
+ const { project_id, readme_content } = parseArgs(args, updateProjectReadmeSchema);
83
123
  const apiClient = getApiClient();
84
124
  const response = await apiClient.updateProjectReadme(project_id, readme_content);
85
125
  if (!response.ok) {
86
- throw new Error(response.error || 'Failed to update README');
126
+ return { result: { error: response.error || 'Failed to update README' }, isError: true };
87
127
  }
88
128
  return { result: response.data };
89
129
  };
@@ -8,17 +8,26 @@
8
8
  *
9
9
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
10
10
  */
11
- import { validateRequired, validateUUID } from '../validators.js';
11
+ import { parseArgs, uuidValidator } from '../validators.js';
12
12
  import { getApiClient } from '../api-client.js';
13
+ // Argument schemas for type-safe parsing
14
+ const getPendingRequestsSchema = {
15
+ project_id: { type: 'string', required: true, validate: uuidValidator },
16
+ };
17
+ const acknowledgeRequestSchema = {
18
+ request_id: { type: 'string', required: true, validate: uuidValidator },
19
+ };
20
+ const answerQuestionSchema = {
21
+ request_id: { type: 'string', required: true, validate: uuidValidator },
22
+ answer: { type: 'string', required: true },
23
+ };
13
24
  export const getPendingRequests = async (args, ctx) => {
14
- const { project_id } = args;
15
- validateRequired(project_id, 'project_id');
16
- validateUUID(project_id, 'project_id');
25
+ const { project_id } = parseArgs(args, getPendingRequestsSchema);
17
26
  const { session } = ctx;
18
27
  const apiClient = getApiClient();
19
28
  const response = await apiClient.getPendingRequests(project_id, session.currentSessionId || undefined);
20
29
  if (!response.ok) {
21
- throw new Error(`Failed to get pending requests: ${response.error}`);
30
+ return { result: { error: response.error || 'Failed to get pending requests' }, isError: true };
22
31
  }
23
32
  return {
24
33
  result: {
@@ -28,14 +37,12 @@ export const getPendingRequests = async (args, ctx) => {
28
37
  };
29
38
  };
30
39
  export const acknowledgeRequest = async (args, ctx) => {
31
- const { request_id } = args;
32
- validateRequired(request_id, 'request_id');
33
- validateUUID(request_id, 'request_id');
40
+ const { request_id } = parseArgs(args, acknowledgeRequestSchema);
34
41
  const { session } = ctx;
35
42
  const apiClient = getApiClient();
36
43
  const response = await apiClient.acknowledgeRequest(request_id, session.currentSessionId || undefined);
37
44
  if (!response.ok) {
38
- throw new Error(`Failed to acknowledge request: ${response.error}`);
45
+ return { result: { error: response.error || 'Failed to acknowledge request' }, isError: true };
39
46
  }
40
47
  return {
41
48
  result: {
@@ -44,15 +51,12 @@ export const acknowledgeRequest = async (args, ctx) => {
44
51
  };
45
52
  };
46
53
  export const answerQuestion = async (args, ctx) => {
47
- const { request_id, answer } = args;
48
- validateRequired(request_id, 'request_id');
49
- validateRequired(answer, 'answer');
50
- validateUUID(request_id, 'request_id');
54
+ const { request_id, answer } = parseArgs(args, answerQuestionSchema);
51
55
  const { session } = ctx;
52
56
  const apiClient = getApiClient();
53
57
  const response = await apiClient.answerQuestion(request_id, answer, session.currentSessionId || undefined);
54
58
  if (!response.ok) {
55
- throw new Error(`Failed to answer question: ${response.error}`);
59
+ return { result: { error: response.error || 'Failed to answer question' }, isError: true };
56
60
  }
57
61
  return {
58
62
  result: {
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Role Management Handlers
3
+ *
4
+ * Handles agent role configuration and assignment:
5
+ * - get_role_settings: Get role configuration for a project
6
+ * - update_role_settings: Update role settings for a project
7
+ * - set_session_role: Set the role for the current session
8
+ * - get_agents_by_role: Get active agents grouped by their assigned roles
9
+ */
10
+ import type { Handler, HandlerRegistry } from './types.js';
11
+ export declare const getRoleSettings: Handler;
12
+ export declare const updateRoleSettings: Handler;
13
+ export declare const setSessionRole: Handler;
14
+ export declare const getAgentsByRole: Handler;
15
+ /**
16
+ * Role handlers registry
17
+ */
18
+ export declare const roleHandlers: HandlerRegistry;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Role Management Handlers
3
+ *
4
+ * Handles agent role configuration and assignment:
5
+ * - get_role_settings: Get role configuration for a project
6
+ * - update_role_settings: Update role settings for a project
7
+ * - set_session_role: Set the role for the current session
8
+ * - get_agents_by_role: Get active agents grouped by their assigned roles
9
+ */
10
+ import { getApiClient } from '../api-client.js';
11
+ export const getRoleSettings = async (args, _ctx) => {
12
+ const { project_id } = args;
13
+ if (!project_id) {
14
+ return {
15
+ result: { error: 'project_id is required' },
16
+ };
17
+ }
18
+ const apiClient = getApiClient();
19
+ const response = await apiClient.proxy('get_role_settings', { project_id });
20
+ if (!response.ok) {
21
+ return {
22
+ result: { error: response.error || 'Failed to get role settings' },
23
+ };
24
+ }
25
+ return { result: response.data };
26
+ };
27
+ export const updateRoleSettings = async (args, _ctx) => {
28
+ const { project_id, role, enabled, display_name, description, priority_filter, fallback_activities, auto_assign_validation, auto_assign_deployment, } = args;
29
+ if (!project_id) {
30
+ return {
31
+ result: { error: 'project_id is required' },
32
+ };
33
+ }
34
+ if (!role) {
35
+ return {
36
+ result: { error: 'role is required' },
37
+ };
38
+ }
39
+ const apiClient = getApiClient();
40
+ const response = await apiClient.proxy('update_role_settings', {
41
+ project_id,
42
+ role,
43
+ enabled,
44
+ display_name,
45
+ description,
46
+ priority_filter,
47
+ fallback_activities,
48
+ auto_assign_validation,
49
+ auto_assign_deployment,
50
+ });
51
+ if (!response.ok) {
52
+ return {
53
+ result: { error: response.error || 'Failed to update role settings' },
54
+ };
55
+ }
56
+ return { result: response.data };
57
+ };
58
+ export const setSessionRole = async (args, ctx) => {
59
+ const { role, role_config } = args;
60
+ const { session, updateSession } = ctx;
61
+ if (!role) {
62
+ return {
63
+ result: { error: 'role is required' },
64
+ };
65
+ }
66
+ const validRoles = ['developer', 'validator', 'deployer', 'reviewer', 'maintainer'];
67
+ if (!validRoles.includes(role)) {
68
+ return {
69
+ result: {
70
+ error: `Invalid role: ${role}. Must be one of: ${validRoles.join(', ')}`,
71
+ },
72
+ };
73
+ }
74
+ // Update local session state
75
+ updateSession({ currentRole: role });
76
+ // If there's an active session, update it on the server too
77
+ if (session.currentSessionId) {
78
+ const apiClient = getApiClient();
79
+ const response = await apiClient.proxy('set_session_role', {
80
+ session_id: session.currentSessionId,
81
+ role,
82
+ role_config,
83
+ });
84
+ if (!response.ok) {
85
+ return {
86
+ result: { error: response.error || 'Failed to update session role' },
87
+ };
88
+ }
89
+ return {
90
+ result: {
91
+ success: true,
92
+ session_id: session.currentSessionId,
93
+ role,
94
+ message: `Session role updated to ${role}. Task filtering and fallback suggestions will now be optimized for this role.`,
95
+ },
96
+ };
97
+ }
98
+ return {
99
+ result: {
100
+ success: true,
101
+ role,
102
+ message: `Local role set to ${role}. Start a session to persist this role.`,
103
+ },
104
+ };
105
+ };
106
+ export const getAgentsByRole = async (args, _ctx) => {
107
+ const { project_id } = args;
108
+ if (!project_id) {
109
+ return {
110
+ result: { error: 'project_id is required' },
111
+ };
112
+ }
113
+ const apiClient = getApiClient();
114
+ const response = await apiClient.proxy('get_agents_by_role', { project_id });
115
+ if (!response.ok) {
116
+ return {
117
+ result: { error: response.error || 'Failed to get agents by role' },
118
+ };
119
+ }
120
+ return { result: response.data };
121
+ };
122
+ /**
123
+ * Role handlers registry
124
+ */
125
+ export const roleHandlers = {
126
+ get_role_settings: getRoleSettings,
127
+ update_role_settings: updateRoleSettings,
128
+ set_session_role: setSessionRole,
129
+ get_agents_by_role: getAgentsByRole,
130
+ };
@@ -8,13 +8,35 @@
8
8
  * - get_help
9
9
  * - get_token_usage
10
10
  */
11
+ import { parseArgs, createEnumValidator } from '../validators.js';
11
12
  import { getApiClient } from '../api-client.js';
13
+ const VALID_MODES = ['lite', 'full'];
14
+ const VALID_MODELS = ['opus', 'sonnet', 'haiku'];
15
+ const VALID_ROLES = ['developer', 'validator', 'deployer', 'reviewer', 'maintainer'];
16
+ // Argument schemas for type-safe parsing
17
+ const startWorkSessionSchema = {
18
+ project_id: { type: 'string' },
19
+ git_url: { type: 'string' },
20
+ mode: { type: 'string', default: 'lite', validate: createEnumValidator(VALID_MODES) },
21
+ model: { type: 'string', validate: createEnumValidator(VALID_MODELS) },
22
+ role: { type: 'string', default: 'developer', validate: createEnumValidator(VALID_ROLES) },
23
+ };
24
+ const heartbeatSchema = {
25
+ session_id: { type: 'string' },
26
+ current_worktree_path: { type: 'string' },
27
+ };
28
+ const endWorkSessionSchema = {
29
+ session_id: { type: 'string' },
30
+ };
31
+ const getHelpSchema = {
32
+ topic: { type: 'string', required: true },
33
+ };
12
34
  export const startWorkSession = async (args, ctx) => {
13
- const { project_id, git_url, mode = 'lite', model, role = 'developer' } = args;
35
+ const { project_id, git_url, mode, model, role } = parseArgs(args, startWorkSessionSchema);
14
36
  const { session, updateSession } = ctx;
15
37
  // Reset token tracking for new session with model info
16
38
  const normalizedModel = model ? model.toLowerCase().replace(/^claude[- ]*/i, '') : null;
17
- const validModel = normalizedModel && ['opus', 'sonnet', 'haiku'].includes(normalizedModel)
39
+ const validModel = normalizedModel && VALID_MODELS.includes(normalizedModel)
18
40
  ? normalizedModel
19
41
  : null;
20
42
  updateSession({
@@ -38,9 +60,9 @@ export const startWorkSession = async (args, ctx) => {
38
60
  const response = await apiClient.startSession({
39
61
  project_id,
40
62
  git_url,
41
- mode,
42
- model,
43
- role
63
+ mode: mode,
64
+ model: model,
65
+ role: role
44
66
  });
45
67
  if (!response.ok) {
46
68
  return {
@@ -61,20 +83,33 @@ export const startWorkSession = async (args, ctx) => {
61
83
  currentPersona: data.persona || null,
62
84
  });
63
85
  }
64
- // 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
65
89
  const result = {
66
90
  session_started: true,
67
- directive: data.directive || 'ACTION_REQUIRED: Start working immediately.',
68
- auto_continue: true,
69
- session_id: data.session_id,
70
- persona: data.persona,
71
- role: data.role,
72
- project: data.project,
73
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;
74
104
  // Add task data
75
105
  if (data.next_task) {
76
106
  result.next_task = data.next_task;
77
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
+ }
78
113
  // Add active tasks for full mode
79
114
  if (data.active_tasks) {
80
115
  result.active_tasks = data.active_tasks;
@@ -103,8 +138,14 @@ export const startWorkSession = async (args, ctx) => {
103
138
  worktree_hint: 'CRITICAL: Create a git worktree before starting work. Run get_help("git") for instructions.',
104
139
  };
105
140
  }
106
- // Add next action at end
107
- 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) {
108
149
  result.next_action = `update_task(task_id: "${data.next_task.id}", status: "in_progress")`;
109
150
  }
110
151
  else if (data.project) {
@@ -113,7 +154,7 @@ export const startWorkSession = async (args, ctx) => {
113
154
  return { result };
114
155
  };
115
156
  export const heartbeat = async (args, ctx) => {
116
- const { session_id, current_worktree_path } = args;
157
+ const { session_id, current_worktree_path } = parseArgs(args, heartbeatSchema);
117
158
  const { session } = ctx;
118
159
  const targetSession = session_id || session.currentSessionId;
119
160
  if (!targetSession) {
@@ -150,7 +191,7 @@ export const heartbeat = async (args, ctx) => {
150
191
  };
151
192
  };
152
193
  export const endWorkSession = async (args, ctx) => {
153
- const { session_id } = args;
194
+ const { session_id } = parseArgs(args, endWorkSessionSchema);
154
195
  const { session, updateSession } = ctx;
155
196
  const targetSession = session_id || session.currentSessionId;
156
197
  if (!targetSession) {
@@ -205,7 +246,7 @@ export const endWorkSession = async (args, ctx) => {
205
246
  };
206
247
  };
207
248
  export const getHelp = async (args, _ctx) => {
208
- const { topic } = args;
249
+ const { topic } = parseArgs(args, getHelpSchema);
209
250
  const apiClient = getApiClient();
210
251
  const response = await apiClient.getHelpTopic(topic);
211
252
  if (!response.ok) {