@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
@@ -13,21 +13,105 @@
13
13
  * - complete_deployment_requirement
14
14
  * - get_deployment_requirements
15
15
  */
16
- import { ValidationError, validateRequired, validateUUID, validateEnvironment, } from '../validators.js';
16
+ import { ValidationError, parseArgs, uuidValidator, createEnumValidator, } from '../validators.js';
17
17
  import { getApiClient } from '../api-client.js';
18
+ const ENVIRONMENTS = ['development', 'staging', 'production'];
19
+ const VERSION_BUMPS = ['patch', 'minor', 'major'];
20
+ const REQUIREMENT_TYPES = ['migration', 'env_var', 'config', 'manual', 'breaking_change', 'agent_task'];
21
+ const REQUIREMENT_STAGES = ['preparation', 'deployment', 'verification'];
22
+ const REQUIREMENT_STATUSES = ['pending', 'completed', 'converted_to_task', 'all'];
23
+ const SCHEDULE_TYPES = ['once', 'hourly', 'daily', 'weekly', 'monthly'];
24
+ // ============================================================================
25
+ // Argument Schemas
26
+ // ============================================================================
27
+ const requestDeploymentSchema = {
28
+ project_id: { type: 'string', required: true, validate: uuidValidator },
29
+ environment: { type: 'string', default: 'production', validate: createEnumValidator(ENVIRONMENTS) },
30
+ version_bump: { type: 'string', default: 'patch', validate: createEnumValidator(VERSION_BUMPS) },
31
+ notes: { type: 'string' },
32
+ git_ref: { type: 'string' },
33
+ };
34
+ const claimDeploymentValidationSchema = {
35
+ project_id: { type: 'string', required: true, validate: uuidValidator },
36
+ };
37
+ const reportValidationSchema = {
38
+ project_id: { type: 'string', required: true, validate: uuidValidator },
39
+ build_passed: { type: 'boolean', required: true },
40
+ tests_passed: { type: 'boolean' },
41
+ error_message: { type: 'string' },
42
+ };
43
+ const checkDeploymentStatusSchema = {
44
+ project_id: { type: 'string', required: true, validate: uuidValidator },
45
+ };
46
+ const startDeploymentSchema = {
47
+ project_id: { type: 'string', required: true, validate: uuidValidator },
48
+ };
49
+ const completeDeploymentSchema = {
50
+ project_id: { type: 'string', required: true, validate: uuidValidator },
51
+ success: { type: 'boolean', required: true },
52
+ summary: { type: 'string' },
53
+ };
54
+ const cancelDeploymentSchema = {
55
+ project_id: { type: 'string', required: true, validate: uuidValidator },
56
+ reason: { type: 'string' },
57
+ };
58
+ const addDeploymentRequirementSchema = {
59
+ project_id: { type: 'string', required: true, validate: uuidValidator },
60
+ type: { type: 'string', required: true, validate: createEnumValidator(REQUIREMENT_TYPES) },
61
+ title: { type: 'string', required: true },
62
+ description: { type: 'string' },
63
+ file_path: { type: 'string' },
64
+ stage: { type: 'string', default: 'preparation', validate: createEnumValidator(REQUIREMENT_STAGES) },
65
+ blocking: { type: 'boolean', default: false },
66
+ recurring: { type: 'boolean', default: false },
67
+ };
68
+ const completeDeploymentRequirementSchema = {
69
+ requirement_id: { type: 'string', required: true, validate: uuidValidator },
70
+ };
71
+ const getDeploymentRequirementsSchema = {
72
+ project_id: { type: 'string', required: true, validate: uuidValidator },
73
+ status: { type: 'string', default: 'pending', validate: createEnumValidator(REQUIREMENT_STATUSES) },
74
+ stage: { type: 'string', validate: createEnumValidator([...REQUIREMENT_STAGES, 'all']) },
75
+ };
76
+ const scheduleDeploymentSchema = {
77
+ project_id: { type: 'string', required: true, validate: uuidValidator },
78
+ environment: { type: 'string', default: 'production', validate: createEnumValidator(ENVIRONMENTS) },
79
+ version_bump: { type: 'string', default: 'patch', validate: createEnumValidator(VERSION_BUMPS) },
80
+ schedule_type: { type: 'string', default: 'once', validate: createEnumValidator(SCHEDULE_TYPES) },
81
+ scheduled_at: { type: 'string', required: true },
82
+ auto_trigger: { type: 'boolean', default: true },
83
+ hours_interval: { type: 'number', default: 1 },
84
+ notes: { type: 'string' },
85
+ git_ref: { type: 'string' },
86
+ };
87
+ const getScheduledDeploymentsSchema = {
88
+ project_id: { type: 'string', required: true, validate: uuidValidator },
89
+ include_disabled: { type: 'boolean', default: false },
90
+ };
91
+ const updateScheduledDeploymentSchema = {
92
+ schedule_id: { type: 'string', required: true, validate: uuidValidator },
93
+ environment: { type: 'string', validate: createEnumValidator(ENVIRONMENTS) },
94
+ version_bump: { type: 'string', validate: createEnumValidator(VERSION_BUMPS) },
95
+ schedule_type: { type: 'string', validate: createEnumValidator(SCHEDULE_TYPES) },
96
+ scheduled_at: { type: 'string' },
97
+ auto_trigger: { type: 'boolean' },
98
+ hours_interval: { type: 'number' },
99
+ enabled: { type: 'boolean' },
100
+ notes: { type: 'string' },
101
+ git_ref: { type: 'string' },
102
+ };
103
+ const deleteScheduledDeploymentSchema = {
104
+ schedule_id: { type: 'string', required: true, validate: uuidValidator },
105
+ };
106
+ const triggerScheduledDeploymentSchema = {
107
+ schedule_id: { type: 'string', required: true, validate: uuidValidator },
108
+ };
109
+ const checkDueDeploymentsSchema = {
110
+ project_id: { type: 'string', required: true, validate: uuidValidator },
111
+ };
18
112
  export const requestDeployment = async (args, ctx) => {
19
- const { project_id, environment = 'production', version_bump = 'patch', notes, git_ref } = args;
113
+ const { project_id, environment, version_bump, notes, git_ref } = parseArgs(args, requestDeploymentSchema);
20
114
  const { session } = ctx;
21
- validateRequired(project_id, 'project_id');
22
- validateUUID(project_id, 'project_id');
23
- validateEnvironment(environment);
24
- if (version_bump && !['patch', 'minor', 'major'].includes(version_bump)) {
25
- throw new ValidationError('Invalid version_bump value', {
26
- field: 'version_bump',
27
- validValues: ['patch', 'minor', 'major'],
28
- hint: 'Must be one of: patch, minor, major',
29
- });
30
- }
31
115
  const apiClient = getApiClient();
32
116
  const response = await apiClient.requestDeployment(project_id, {
33
117
  environment,
@@ -36,33 +120,23 @@ export const requestDeployment = async (args, ctx) => {
36
120
  git_ref
37
121
  });
38
122
  if (!response.ok) {
39
- throw new Error(response.error || 'Failed to request deployment');
123
+ return { result: { error: response.error || 'Failed to request deployment' }, isError: true };
40
124
  }
41
125
  return { result: response.data };
42
126
  };
43
127
  export const claimDeploymentValidation = async (args, ctx) => {
44
- const { project_id } = args;
128
+ const { project_id } = parseArgs(args, claimDeploymentValidationSchema);
45
129
  const { session } = ctx;
46
- validateRequired(project_id, 'project_id');
47
- validateUUID(project_id, 'project_id');
48
130
  const apiClient = getApiClient();
49
131
  const response = await apiClient.claimDeploymentValidation(project_id, session.currentSessionId || undefined);
50
132
  if (!response.ok) {
51
- throw new Error(response.error || 'Failed to claim deployment validation');
133
+ return { result: { error: response.error || 'Failed to claim deployment validation' }, isError: true };
52
134
  }
53
135
  return { result: response.data };
54
136
  };
55
137
  export const reportValidation = async (args, ctx) => {
56
- const { project_id, build_passed, tests_passed, error_message } = args;
138
+ const { project_id, build_passed, tests_passed, error_message } = parseArgs(args, reportValidationSchema);
57
139
  const { session } = ctx;
58
- validateRequired(project_id, 'project_id');
59
- validateUUID(project_id, 'project_id');
60
- if (build_passed === undefined) {
61
- throw new ValidationError('build_passed is required', {
62
- field: 'build_passed',
63
- hint: 'Set to true if the build succeeded, false otherwise',
64
- });
65
- }
66
140
  const apiClient = getApiClient();
67
141
  const response = await apiClient.reportValidation(project_id, {
68
142
  build_passed,
@@ -70,79 +144,53 @@ export const reportValidation = async (args, ctx) => {
70
144
  error_message
71
145
  });
72
146
  if (!response.ok) {
73
- throw new Error(response.error || 'Failed to report validation');
147
+ return { result: { error: response.error || 'Failed to report validation' }, isError: true };
74
148
  }
75
149
  return { result: response.data };
76
150
  };
77
151
  export const checkDeploymentStatus = async (args, ctx) => {
78
- const { project_id } = args;
79
- validateRequired(project_id, 'project_id');
80
- validateUUID(project_id, 'project_id');
152
+ const { project_id } = parseArgs(args, checkDeploymentStatusSchema);
81
153
  const apiClient = getApiClient();
82
154
  const response = await apiClient.checkDeploymentStatus(project_id);
83
155
  if (!response.ok) {
84
- throw new Error(response.error || 'Failed to check deployment status');
156
+ return { result: { error: response.error || 'Failed to check deployment status' }, isError: true };
85
157
  }
86
158
  return { result: response.data };
87
159
  };
88
160
  export const startDeployment = async (args, ctx) => {
89
- const { project_id } = args;
161
+ const { project_id } = parseArgs(args, startDeploymentSchema);
90
162
  const { session } = ctx;
91
- validateRequired(project_id, 'project_id');
92
- validateUUID(project_id, 'project_id');
93
163
  const apiClient = getApiClient();
94
164
  const response = await apiClient.startDeployment(project_id, session.currentSessionId || undefined);
95
165
  if (!response.ok) {
96
- throw new Error(response.error || 'Failed to start deployment');
166
+ return { result: { error: response.error || 'Failed to start deployment' }, isError: true };
97
167
  }
98
168
  return { result: response.data };
99
169
  };
100
170
  export const completeDeployment = async (args, ctx) => {
101
- const { project_id, success, summary } = args;
171
+ const { project_id, success, summary } = parseArgs(args, completeDeploymentSchema);
102
172
  const { session } = ctx;
103
- validateRequired(project_id, 'project_id');
104
- validateUUID(project_id, 'project_id');
105
- if (success === undefined) {
106
- throw new ValidationError('success is required', {
107
- field: 'success',
108
- hint: 'Set to true if deployment succeeded, false otherwise',
109
- });
110
- }
111
173
  const apiClient = getApiClient();
112
174
  const response = await apiClient.completeDeployment(project_id, {
113
175
  success,
114
176
  summary
115
177
  });
116
178
  if (!response.ok) {
117
- throw new Error(response.error || 'Failed to complete deployment');
179
+ return { result: { error: response.error || 'Failed to complete deployment' }, isError: true };
118
180
  }
119
181
  return { result: response.data };
120
182
  };
121
183
  export const cancelDeployment = async (args, ctx) => {
122
- const { project_id, reason } = args;
123
- validateRequired(project_id, 'project_id');
124
- validateUUID(project_id, 'project_id');
184
+ const { project_id, reason } = parseArgs(args, cancelDeploymentSchema);
125
185
  const apiClient = getApiClient();
126
186
  const response = await apiClient.cancelDeployment(project_id, reason);
127
187
  if (!response.ok) {
128
- throw new Error(response.error || 'Failed to cancel deployment');
188
+ return { result: { error: response.error || 'Failed to cancel deployment' }, isError: true };
129
189
  }
130
190
  return { result: response.data };
131
191
  };
132
192
  export const addDeploymentRequirement = async (args, ctx) => {
133
- const { project_id, type, title, description, file_path, stage = 'preparation', blocking = false, recurring = false } = args;
134
- validateRequired(project_id, 'project_id');
135
- validateUUID(project_id, 'project_id');
136
- validateRequired(type, 'type');
137
- validateRequired(title, 'title');
138
- const validTypes = ['migration', 'env_var', 'config', 'manual', 'breaking_change', 'agent_task'];
139
- if (!validTypes.includes(type)) {
140
- throw new ValidationError(`type must be one of: ${validTypes.join(', ')}`);
141
- }
142
- const validStages = ['preparation', 'deployment', 'verification'];
143
- if (!validStages.includes(stage)) {
144
- throw new ValidationError(`stage must be one of: ${validStages.join(', ')}`);
145
- }
193
+ const { project_id, type, title, description, file_path, stage, blocking, recurring } = parseArgs(args, addDeploymentRequirementSchema);
146
194
  const apiClient = getApiClient();
147
195
  const response = await apiClient.addDeploymentRequirement(project_id, {
148
196
  type: type,
@@ -154,32 +202,28 @@ export const addDeploymentRequirement = async (args, ctx) => {
154
202
  recurring
155
203
  });
156
204
  if (!response.ok) {
157
- throw new Error(response.error || 'Failed to add deployment requirement');
205
+ return { result: { error: response.error || 'Failed to add deployment requirement' }, isError: true };
158
206
  }
159
207
  return { result: response.data };
160
208
  };
161
209
  export const completeDeploymentRequirement = async (args, ctx) => {
162
- const { requirement_id } = args;
163
- validateRequired(requirement_id, 'requirement_id');
164
- validateUUID(requirement_id, 'requirement_id');
210
+ const { requirement_id } = parseArgs(args, completeDeploymentRequirementSchema);
165
211
  const apiClient = getApiClient();
166
212
  const response = await apiClient.completeDeploymentRequirement(requirement_id);
167
213
  if (!response.ok) {
168
- throw new Error(response.error || 'Failed to complete deployment requirement');
214
+ return { result: { error: response.error || 'Failed to complete deployment requirement' }, isError: true };
169
215
  }
170
216
  return { result: response.data };
171
217
  };
172
218
  export const getDeploymentRequirements = async (args, ctx) => {
173
- const { project_id, status = 'pending', stage } = args;
174
- validateRequired(project_id, 'project_id');
175
- validateUUID(project_id, 'project_id');
219
+ const { project_id, status, stage } = parseArgs(args, getDeploymentRequirementsSchema);
176
220
  const apiClient = getApiClient();
177
221
  const response = await apiClient.getDeploymentRequirements(project_id, {
178
222
  status: status,
179
223
  stage: stage
180
224
  });
181
225
  if (!response.ok) {
182
- throw new Error(response.error || 'Failed to get deployment requirements');
226
+ return { result: { error: response.error || 'Failed to get deployment requirements' }, isError: true };
183
227
  }
184
228
  return { result: response.data };
185
229
  };
@@ -187,23 +231,7 @@ export const getDeploymentRequirements = async (args, ctx) => {
187
231
  // Scheduled Deployments
188
232
  // ============================================================================
189
233
  export const scheduleDeployment = async (args, ctx) => {
190
- const { project_id, environment = 'production', version_bump = 'patch', schedule_type = 'once', scheduled_at, auto_trigger = true, notes, git_ref, } = args;
191
- validateRequired(project_id, 'project_id');
192
- validateUUID(project_id, 'project_id');
193
- validateRequired(scheduled_at, 'scheduled_at');
194
- validateEnvironment(environment);
195
- if (!['patch', 'minor', 'major'].includes(version_bump)) {
196
- throw new ValidationError('Invalid version_bump value', {
197
- field: 'version_bump',
198
- validValues: ['patch', 'minor', 'major'],
199
- });
200
- }
201
- if (!['once', 'daily', 'weekly', 'monthly'].includes(schedule_type)) {
202
- throw new ValidationError('Invalid schedule_type value', {
203
- field: 'schedule_type',
204
- validValues: ['once', 'daily', 'weekly', 'monthly'],
205
- });
206
- }
234
+ const { project_id, environment, version_bump, schedule_type, scheduled_at, auto_trigger, hours_interval, notes, git_ref, } = parseArgs(args, scheduleDeploymentSchema);
207
235
  // Parse and validate scheduled_at
208
236
  const scheduledDate = new Date(scheduled_at);
209
237
  if (isNaN(scheduledDate.getTime())) {
@@ -217,6 +245,13 @@ export const scheduleDeployment = async (args, ctx) => {
217
245
  field: 'scheduled_at',
218
246
  });
219
247
  }
248
+ // Validate hours_interval for hourly schedule type (default is 1)
249
+ const hoursInterval = hours_interval ?? 1;
250
+ if (schedule_type === 'hourly' && (hoursInterval < 1 || hoursInterval > 24)) {
251
+ throw new ValidationError('hours_interval must be between 1 and 24', {
252
+ field: 'hours_interval',
253
+ });
254
+ }
220
255
  const apiClient = getApiClient();
221
256
  const response = await apiClient.scheduleDeployment(project_id, {
222
257
  environment: environment,
@@ -224,46 +259,33 @@ export const scheduleDeployment = async (args, ctx) => {
224
259
  schedule_type: schedule_type,
225
260
  scheduled_at: scheduledDate.toISOString(),
226
261
  auto_trigger,
262
+ hours_interval: hoursInterval,
227
263
  notes,
228
264
  git_ref
229
265
  });
230
266
  if (!response.ok) {
231
- throw new Error(response.error || 'Failed to schedule deployment');
267
+ return { result: { error: response.error || 'Failed to schedule deployment' }, isError: true };
232
268
  }
233
269
  return { result: response.data };
234
270
  };
235
271
  export const getScheduledDeployments = async (args, ctx) => {
236
- const { project_id, include_disabled = false } = args;
237
- validateRequired(project_id, 'project_id');
238
- validateUUID(project_id, 'project_id');
272
+ const { project_id, include_disabled } = parseArgs(args, getScheduledDeploymentsSchema);
239
273
  const apiClient = getApiClient();
240
274
  const response = await apiClient.getScheduledDeployments(project_id, include_disabled);
241
275
  if (!response.ok) {
242
- throw new Error(response.error || 'Failed to get scheduled deployments');
276
+ return { result: { error: response.error || 'Failed to get scheduled deployments' }, isError: true };
243
277
  }
244
278
  return { result: response.data };
245
279
  };
246
280
  export const updateScheduledDeployment = async (args, ctx) => {
247
- const { schedule_id, environment, version_bump, schedule_type, scheduled_at, auto_trigger, enabled, notes, git_ref, } = args;
248
- validateRequired(schedule_id, 'schedule_id');
249
- validateUUID(schedule_id, 'schedule_id');
281
+ const { schedule_id, environment, version_bump, schedule_type, scheduled_at, auto_trigger, hours_interval, enabled, notes, git_ref, } = parseArgs(args, updateScheduledDeploymentSchema);
250
282
  const updates = {};
251
- if (environment !== undefined) {
252
- validateEnvironment(environment);
283
+ if (environment !== undefined)
253
284
  updates.environment = environment;
254
- }
255
- if (version_bump !== undefined) {
256
- if (!['patch', 'minor', 'major'].includes(version_bump)) {
257
- throw new ValidationError('Invalid version_bump value');
258
- }
285
+ if (version_bump !== undefined)
259
286
  updates.version_bump = version_bump;
260
- }
261
- if (schedule_type !== undefined) {
262
- if (!['once', 'daily', 'weekly', 'monthly'].includes(schedule_type)) {
263
- throw new ValidationError('Invalid schedule_type value');
264
- }
287
+ if (schedule_type !== undefined)
265
288
  updates.schedule_type = schedule_type;
266
- }
267
289
  if (scheduled_at !== undefined) {
268
290
  const scheduledDate = new Date(scheduled_at);
269
291
  if (isNaN(scheduledDate.getTime())) {
@@ -273,6 +295,12 @@ export const updateScheduledDeployment = async (args, ctx) => {
273
295
  }
274
296
  if (auto_trigger !== undefined)
275
297
  updates.auto_trigger = auto_trigger;
298
+ if (hours_interval !== undefined) {
299
+ if (hours_interval < 1 || hours_interval > 24) {
300
+ throw new ValidationError('hours_interval must be between 1 and 24');
301
+ }
302
+ updates.hours_interval = hours_interval;
303
+ }
276
304
  if (enabled !== undefined)
277
305
  updates.enabled = enabled;
278
306
  if (notes !== undefined)
@@ -285,41 +313,35 @@ export const updateScheduledDeployment = async (args, ctx) => {
285
313
  const apiClient = getApiClient();
286
314
  const response = await apiClient.updateScheduledDeployment(schedule_id, updates);
287
315
  if (!response.ok) {
288
- throw new Error(response.error || 'Failed to update scheduled deployment');
316
+ return { result: { error: response.error || 'Failed to update scheduled deployment' }, isError: true };
289
317
  }
290
318
  return { result: response.data };
291
319
  };
292
320
  export const deleteScheduledDeployment = async (args, ctx) => {
293
- const { schedule_id } = args;
294
- validateRequired(schedule_id, 'schedule_id');
295
- validateUUID(schedule_id, 'schedule_id');
321
+ const { schedule_id } = parseArgs(args, deleteScheduledDeploymentSchema);
296
322
  const apiClient = getApiClient();
297
323
  const response = await apiClient.deleteScheduledDeployment(schedule_id);
298
324
  if (!response.ok) {
299
- throw new Error(response.error || 'Failed to delete scheduled deployment');
325
+ return { result: { error: response.error || 'Failed to delete scheduled deployment' }, isError: true };
300
326
  }
301
327
  return { result: response.data };
302
328
  };
303
329
  export const triggerScheduledDeployment = async (args, ctx) => {
304
- const { schedule_id } = args;
330
+ const { schedule_id } = parseArgs(args, triggerScheduledDeploymentSchema);
305
331
  const { session } = ctx;
306
- validateRequired(schedule_id, 'schedule_id');
307
- validateUUID(schedule_id, 'schedule_id');
308
332
  const apiClient = getApiClient();
309
333
  const response = await apiClient.triggerScheduledDeployment(schedule_id, session.currentSessionId || undefined);
310
334
  if (!response.ok) {
311
- throw new Error(response.error || 'Failed to trigger scheduled deployment');
335
+ return { result: { error: response.error || 'Failed to trigger scheduled deployment' }, isError: true };
312
336
  }
313
337
  return { result: response.data };
314
338
  };
315
339
  export const checkDueDeployments = async (args, ctx) => {
316
- const { project_id } = args;
317
- validateRequired(project_id, 'project_id');
318
- validateUUID(project_id, 'project_id');
340
+ const { project_id } = parseArgs(args, checkDueDeploymentsSchema);
319
341
  const apiClient = getApiClient();
320
342
  const response = await apiClient.checkDueDeployments(project_id);
321
343
  if (!response.ok) {
322
- throw new Error(response.error || 'Failed to check due deployments');
344
+ return { result: { error: response.error || 'Failed to check due deployments' }, isError: true };
323
345
  }
324
346
  return { result: response.data };
325
347
  };
@@ -9,6 +9,13 @@
9
9
  * This saves ~8,000 tokens per schema load.
10
10
  */
11
11
  import type { Handler, HandlerRegistry } from './types.js';
12
+ export declare const TOOL_CATEGORIES: Record<string, {
13
+ description: string;
14
+ tools: Array<{
15
+ name: string;
16
+ brief: string;
17
+ }>;
18
+ }>;
12
19
  export declare const discoverTools: Handler;
13
20
  export declare const getToolInfo: Handler;
14
21
  /**
@@ -8,6 +8,14 @@
8
8
  * Note: Tool documentation is lazy-loaded from tool-docs.ts to save tokens.
9
9
  * This saves ~8,000 tokens per schema load.
10
10
  */
11
+ import { parseArgs } from '../validators.js';
12
+ // Argument schemas for type-safe parsing
13
+ const discoverToolsSchema = {
14
+ category: { type: 'string' },
15
+ };
16
+ const getToolInfoSchema = {
17
+ tool_name: { type: 'string', required: true },
18
+ };
11
19
  // Lazy-loaded tool documentation cache
12
20
  let toolInfoCache = null;
13
21
  /**
@@ -22,8 +30,8 @@ async function getToolDocs() {
22
30
  toolInfoCache = TOOL_INFO;
23
31
  return toolInfoCache;
24
32
  }
25
- // Tool categories with brief descriptions
26
- const TOOL_CATEGORIES = {
33
+ // Tool categories with brief descriptions (exported for documentation generation)
34
+ export const TOOL_CATEGORIES = {
27
35
  session: {
28
36
  description: 'Session lifecycle and monitoring',
29
37
  tools: [
@@ -108,6 +116,7 @@ const TOOL_CATEGORIES = {
108
116
  tools: [
109
117
  { name: 'add_finding', brief: 'Record audit finding' },
110
118
  { name: 'get_findings', brief: 'List findings' },
119
+ { name: 'get_findings_stats', brief: 'Get findings statistics' },
111
120
  { name: 'update_finding', brief: 'Update finding status' },
112
121
  { name: 'delete_finding', brief: 'Remove finding' },
113
122
  ],
@@ -161,6 +170,26 @@ const TOOL_CATEGORIES = {
161
170
  { name: 'add_task_to_body_of_work', brief: 'Add task to group' },
162
171
  { name: 'remove_task_from_body_of_work', brief: 'Remove from group' },
163
172
  { name: 'activate_body_of_work', brief: 'Activate for work' },
173
+ { name: 'add_task_dependency', brief: 'Add task dependency' },
174
+ { name: 'remove_task_dependency', brief: 'Remove task dependency' },
175
+ { name: 'get_task_dependencies', brief: 'List task dependencies' },
176
+ { name: 'get_next_body_of_work_task', brief: 'Get next available task' },
177
+ ],
178
+ },
179
+ sprints: {
180
+ description: 'Time-bounded sprints with velocity tracking',
181
+ tools: [
182
+ { name: 'create_sprint', brief: 'Create new sprint' },
183
+ { name: 'update_sprint', brief: 'Update sprint details' },
184
+ { name: 'get_sprint', brief: 'Get sprint with tasks' },
185
+ { name: 'get_sprints', brief: 'List project sprints' },
186
+ { name: 'delete_sprint', brief: 'Delete sprint' },
187
+ { name: 'start_sprint', brief: 'Start sprint' },
188
+ { name: 'complete_sprint', brief: 'Complete sprint' },
189
+ { name: 'add_task_to_sprint', brief: 'Add task to sprint' },
190
+ { name: 'remove_task_from_sprint', brief: 'Remove from sprint' },
191
+ { name: 'get_sprint_backlog', brief: 'Get available tasks' },
192
+ { name: 'get_sprint_velocity', brief: 'Velocity metrics' },
164
193
  ],
165
194
  },
166
195
  requests: {
@@ -198,6 +227,17 @@ const TOOL_CATEGORIES = {
198
227
  { name: 'update_cost_alert', brief: 'Update alert config' },
199
228
  { name: 'delete_cost_alert', brief: 'Remove alert' },
200
229
  { name: 'get_task_costs', brief: 'Cost per task' },
230
+ { name: 'get_body_of_work_costs', brief: 'Cost per body of work' },
231
+ { name: 'get_sprint_costs', brief: 'Cost per sprint' },
232
+ ],
233
+ },
234
+ git_issues: {
235
+ description: 'Git conflict and issue tracking',
236
+ tools: [
237
+ { name: 'add_git_issue', brief: 'Record git issue' },
238
+ { name: 'resolve_git_issue', brief: 'Mark resolved' },
239
+ { name: 'get_git_issues', brief: 'List git issues' },
240
+ { name: 'delete_git_issue', brief: 'Delete git issue' },
201
241
  ],
202
242
  },
203
243
  knowledge: {
@@ -206,9 +246,61 @@ const TOOL_CATEGORIES = {
206
246
  { name: 'query_knowledge_base', brief: 'Aggregated project knowledge in one call' },
207
247
  ],
208
248
  },
249
+ discovery: {
250
+ description: 'Tool discovery and documentation',
251
+ tools: [
252
+ { name: 'discover_tools', brief: 'List tools by category' },
253
+ { name: 'get_tool_info', brief: 'Get detailed tool docs' },
254
+ ],
255
+ },
256
+ subtasks: {
257
+ description: 'Break tasks into smaller pieces',
258
+ tools: [
259
+ { name: 'add_subtask', brief: 'Add subtask to task' },
260
+ { name: 'get_subtasks', brief: 'List task subtasks' },
261
+ ],
262
+ },
263
+ worktrees: {
264
+ description: 'Git worktree management',
265
+ tools: [
266
+ { name: 'get_stale_worktrees', brief: 'Find orphaned worktrees' },
267
+ { name: 'clear_worktree_path', brief: 'Clear worktree from task' },
268
+ ],
269
+ },
270
+ roles: {
271
+ description: 'Agent role management',
272
+ tools: [
273
+ { name: 'get_role_settings', brief: 'Get project role settings' },
274
+ { name: 'update_role_settings', brief: 'Configure role behavior' },
275
+ { name: 'set_session_role', brief: 'Set session role' },
276
+ { name: 'get_agents_by_role', brief: 'List agents by role' },
277
+ ],
278
+ },
279
+ file_locks: {
280
+ description: 'File checkout/locking for multi-agent',
281
+ tools: [
282
+ { name: 'checkout_file', brief: 'Lock file for editing' },
283
+ { name: 'checkin_file', brief: 'Release file lock' },
284
+ { name: 'get_file_checkouts', brief: 'List file locks' },
285
+ { name: 'abandon_checkout', brief: 'Force-release lock' },
286
+ { name: 'is_file_available', brief: 'Check if file is free' },
287
+ ],
288
+ },
289
+ connectors: {
290
+ description: 'External integration connectors',
291
+ tools: [
292
+ { name: 'get_connectors', brief: 'List project connectors' },
293
+ { name: 'get_connector', brief: 'Get connector details' },
294
+ { name: 'add_connector', brief: 'Create new connector' },
295
+ { name: 'update_connector', brief: 'Update connector config' },
296
+ { name: 'delete_connector', brief: 'Remove connector' },
297
+ { name: 'test_connector', brief: 'Send test event' },
298
+ { name: 'get_connector_events', brief: 'Event history' },
299
+ ],
300
+ },
209
301
  };
210
302
  export const discoverTools = async (args) => {
211
- const { category } = args;
303
+ const { category } = parseArgs(args, discoverToolsSchema);
212
304
  if (category) {
213
305
  // Return tools in specific category
214
306
  const cat = TOOL_CATEGORIES[category];
@@ -243,10 +335,7 @@ export const discoverTools = async (args) => {
243
335
  };
244
336
  };
245
337
  export const getToolInfo = async (args) => {
246
- const { tool_name } = args;
247
- if (!tool_name) {
248
- return { result: { error: 'tool_name is required' } };
249
- }
338
+ const { tool_name } = parseArgs(args, getToolInfoSchema);
250
339
  // Lazy-load tool documentation
251
340
  const toolDocs = await getToolDocs();
252
341
  const info = toolDocs[tool_name];